*
*/
-#include <linux/config.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/firmware.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
+#include <linux/libata.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/processor.h>
#include <scsi/scsi_tcq.h>
#include <scsi/scsi_eh.h>
#include <scsi/scsi_cmnd.h>
-#include <scsi/scsi_request.h>
#include "ipr.h"
/*
/* This table describes the differences between DMA controller chips */
static const struct ipr_chip_cfg_t ipr_chip_cfg[] = {
- { /* Gemstone, Citrine, and Obsidian */
+ { /* Gemstone, Citrine, Obsidian, and Obsidian-E */
.mailbox = 0x0042C,
.cache_line_size = 0x20,
{
{ PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CITRINE, &ipr_chip_cfg[0] },
{ PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_OBSIDIAN, &ipr_chip_cfg[0] },
{ PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_OBSIDIAN, &ipr_chip_cfg[0] },
+ { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_OBSIDIAN_E, &ipr_chip_cfg[0] },
{ PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_SNIPE, &ipr_chip_cfg[1] },
{ PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_SCAMP, &ipr_chip_cfg[1] }
};
MODULE_LICENSE("GPL");
MODULE_VERSION(IPR_DRIVER_VERSION);
-static const char *ipr_gpdd_dev_end_states[] = {
- "Command complete",
- "Terminated by host",
- "Terminated by device reset",
- "Terminated by bus reset",
- "Unknown",
- "Command not started"
-};
-
-static const char *ipr_gpdd_dev_bus_phases[] = {
- "Bus free",
- "Arbitration",
- "Selection",
- "Message out",
- "Command",
- "Message in",
- "Data out",
- "Data in",
- "Status",
- "Reselection",
- "Unknown"
-};
-
/* A constant array of IOASCs/URCs/Error Messages */
static const
struct ipr_error_table_t ipr_error_table[] = {
"Qualified success"},
{0x01080000, 1, 1,
"FFFE: Soft device bus error recovered by the IOA"},
+ {0x01088100, 0, 1,
+ "4101: Soft device bus fabric error"},
{0x01170600, 0, 1,
"FFF9: Device sector reassign successful"},
{0x01170900, 0, 1,
"FFFA: Undefined device response recovered by the IOA"},
{0x014A0000, 1, 1,
"FFF6: Device bus error, message or command phase"},
+ {0x014A8000, 0, 1,
+ "FFFE: Task Management Function failed"},
{0x015D0000, 0, 1,
"FFF6: Failure prediction threshold exceeded"},
{0x015D9200, 0, 1,
"3109: IOA timed out a device command"},
{0x04088000, 0, 0,
"3120: SCSI bus is not operational"},
+ {0x04088100, 0, 1,
+ "4100: Hard device bus fabric error"},
{0x04118000, 0, 1,
"9000: IOA reserved area data check"},
{0x04118100, 0, 1,
"Device bus status error"},
{0x04448600, 0, 1,
"8157: IOA error requiring IOA reset to recover"},
+ {0x04448700, 0, 0,
+ "ATA device status error"},
{0x04490000, 0, 0,
"Message reject received from the device"},
{0x04449200, 0, 1,
"9082: IOA detected device error"},
{0x044A0000, 1, 1,
"3110: Device bus error, message or command phase"},
+ {0x044A8000, 1, 1,
+ "3110: SAS Command / Task Management Function failed"},
{0x04670400, 0, 1,
"9091: Incorrect hardware configuration change has been detected"},
{0x04678000, 0, 1,
"9073: Invalid multi-adapter configuration"},
+ {0x04678100, 0, 1,
+ "4010: Incorrect connection between cascaded expanders"},
+ {0x04678200, 0, 1,
+ "4020: Connections exceed IOA design limits"},
+ {0x04678300, 0, 1,
+ "4030: Incorrect multipath connection"},
+ {0x04679000, 0, 1,
+ "4110: Unsupported enclosure function"},
{0x046E0000, 0, 1,
"FFF4: Command to logical unit failed"},
{0x05240000, 1, 0,
"9031: Array protection temporarily suspended, protection resuming"},
{0x06040600, 0, 1,
"9040: Array protection temporarily suspended, protection resuming"},
+ {0x06288000, 0, 1,
+ "3140: Device bus not ready to ready transition"},
{0x06290000, 0, 1,
"FFFB: SCSI bus was reset"},
{0x06290500, 0, 0,
"3150: SCSI bus configuration error"},
{0x06678100, 0, 1,
"9074: Asymmetric advanced function disk configuration"},
+ {0x06678300, 0, 1,
+ "4040: Incomplete multipath connection between IOA and enclosure"},
+ {0x06678400, 0, 1,
+ "4041: Incomplete multipath connection between enclosure and device"},
+ {0x06678500, 0, 1,
+ "9075: Incomplete multipath connection between IOA and remote IOA"},
+ {0x06678600, 0, 1,
+ "9076: Configuration error, missing remote IOA"},
+ {0x06679100, 0, 1,
+ "4050: Enclosure does not support a required multipath function"},
{0x06690200, 0, 1,
"9041: Array protection temporarily suspended"},
{0x06698200, 0, 1,
"9072: Link not operational transition"},
{0x066B8200, 0, 1,
"9032: Array exposed but still protected"},
+ {0x066B9100, 0, 1,
+ "4061: Multipath redundancy level got better"},
+ {0x066B9200, 0, 1,
+ "4060: Multipath redundancy level got worse"},
{0x07270000, 0, 0,
"Failure due to other device"},
{0x07278000, 0, 1,
trace_entry->time = jiffies;
trace_entry->op_code = ipr_cmd->ioarcb.cmd_pkt.cdb[0];
trace_entry->type = type;
- trace_entry->cmd_index = ipr_cmd->cmd_index;
+ trace_entry->ata_op_code = ipr_cmd->ioarcb.add_data.u.regs.command;
+ trace_entry->cmd_index = ipr_cmd->cmd_index & 0xff;
trace_entry->res_handle = ipr_cmd->ioarcb.res_handle;
trace_entry->u.add_data = add_data;
}
ioarcb->read_ioadl_len = 0;
ioasa->ioasc = 0;
ioasa->residual_data_len = 0;
+ ioasa->u.gata.status = 0;
ipr_cmd->scsi_cmd = NULL;
+ ipr_cmd->qc = NULL;
ipr_cmd->sense_buffer[0] = 0;
ipr_cmd->dma_use_sg = 0;
}
return 0;
}
+/**
+ * ipr_sata_eh_done - done function for aborted SATA commands
+ * @ipr_cmd: ipr command struct
+ *
+ * This function is invoked for ops generated to SATA
+ * devices which are being aborted.
+ *
+ * Return value:
+ * none
+ **/
+static void ipr_sata_eh_done(struct ipr_cmnd *ipr_cmd)
+{
+ struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
+ struct ata_queued_cmd *qc = ipr_cmd->qc;
+ struct ipr_sata_port *sata_port = qc->ap->private_data;
+
+ qc->err_mask |= AC_ERR_OTHER;
+ sata_port->ioasa.status |= ATA_BUSY;
+ list_add_tail(&ipr_cmd->queue, &ioa_cfg->free_q);
+ ata_qc_complete(qc);
+}
+
/**
* ipr_scsi_eh_done - mid-layer done function for aborted ops
* @ipr_cmd: ipr command struct
if (ipr_cmd->scsi_cmd)
ipr_cmd->done = ipr_scsi_eh_done;
+ else if (ipr_cmd->qc)
+ ipr_cmd->done = ipr_sata_eh_done;
ipr_trc_hook(ipr_cmd, IPR_TRACE_FINISH, IPR_IOASC_IOA_WAS_RESET);
del_timer(&ipr_cmd->timer);
res->del_from_ml = 0;
res->resetting_device = 0;
res->sdev = NULL;
+ res->sata_port = NULL;
}
/**
if (hostrcb->hcam.notify_type == IPR_HOST_RCB_NOTIF_TYPE_REM_ENTRY) {
if (res->sdev) {
- res->sdev->hostdata = NULL;
res->del_from_ml = 1;
+ res->cfgte.res_handle = IPR_INVALID_RES_HANDLE;
if (ioa_cfg->allow_ml_add_del)
schedule_work(&ioa_cfg->work_q);
} else
/**
* ipr_log_hex_data - Log additional hex IOA error data.
+ * @ioa_cfg: ioa config struct
* @data: IOA error data
* @len: data length
*
* Return value:
* none
**/
-static void ipr_log_hex_data(u32 *data, int len)
+static void ipr_log_hex_data(struct ipr_ioa_cfg *ioa_cfg, u32 *data, int len)
{
int i;
if (len == 0)
return;
+ if (ioa_cfg->log_level <= IPR_DEFAULT_LOG_LEVEL)
+ len = min_t(int, len, IPR_DEFAULT_MAX_ERROR_DUMP);
+
for (i = 0; i < len / 4; i += 4) {
ipr_err("%08X: %08X %08X %08X %08X\n", i*4,
be32_to_cpu(data[i]),
ipr_err("%s\n", error->failure_reason);
ipr_err("Remote Adapter VPD:\n");
ipr_log_ext_vpd(&error->vpd);
- ipr_log_hex_data(error->data,
+ ipr_log_hex_data(ioa_cfg, error->data,
be32_to_cpu(hostrcb->hcam.length) -
(offsetof(struct ipr_hostrcb_error, u) +
offsetof(struct ipr_hostrcb_type_17_error, data)));
ipr_err("%s\n", error->failure_reason);
ipr_err("Remote Adapter VPD:\n");
ipr_log_vpd(&error->vpd);
- ipr_log_hex_data(error->data,
+ ipr_log_hex_data(ioa_cfg, error->data,
be32_to_cpu(hostrcb->hcam.length) -
(offsetof(struct ipr_hostrcb_error, u) +
offsetof(struct ipr_hostrcb_type_07_error, data)));
}
+static const struct {
+ u8 active;
+ char *desc;
+} path_active_desc[] = {
+ { IPR_PATH_NO_INFO, "Path" },
+ { IPR_PATH_ACTIVE, "Active path" },
+ { IPR_PATH_NOT_ACTIVE, "Inactive path" }
+};
+
+static const struct {
+ u8 state;
+ char *desc;
+} path_state_desc[] = {
+ { IPR_PATH_STATE_NO_INFO, "has no path state information available" },
+ { IPR_PATH_HEALTHY, "is healthy" },
+ { IPR_PATH_DEGRADED, "is degraded" },
+ { IPR_PATH_FAILED, "is failed" }
+};
+
+/**
+ * ipr_log_fabric_path - Log a fabric path error
+ * @hostrcb: hostrcb struct
+ * @fabric: fabric descriptor
+ *
+ * Return value:
+ * none
+ **/
+static void ipr_log_fabric_path(struct ipr_hostrcb *hostrcb,
+ struct ipr_hostrcb_fabric_desc *fabric)
+{
+ int i, j;
+ u8 path_state = fabric->path_state;
+ u8 active = path_state & IPR_PATH_ACTIVE_MASK;
+ u8 state = path_state & IPR_PATH_STATE_MASK;
+
+ for (i = 0; i < ARRAY_SIZE(path_active_desc); i++) {
+ if (path_active_desc[i].active != active)
+ continue;
+
+ for (j = 0; j < ARRAY_SIZE(path_state_desc); j++) {
+ if (path_state_desc[j].state != state)
+ continue;
+
+ if (fabric->cascaded_expander == 0xff && fabric->phy == 0xff) {
+ ipr_hcam_err(hostrcb, "%s %s: IOA Port=%d\n",
+ path_active_desc[i].desc, path_state_desc[j].desc,
+ fabric->ioa_port);
+ } else if (fabric->cascaded_expander == 0xff) {
+ ipr_hcam_err(hostrcb, "%s %s: IOA Port=%d, Phy=%d\n",
+ path_active_desc[i].desc, path_state_desc[j].desc,
+ fabric->ioa_port, fabric->phy);
+ } else if (fabric->phy == 0xff) {
+ ipr_hcam_err(hostrcb, "%s %s: IOA Port=%d, Cascade=%d\n",
+ path_active_desc[i].desc, path_state_desc[j].desc,
+ fabric->ioa_port, fabric->cascaded_expander);
+ } else {
+ ipr_hcam_err(hostrcb, "%s %s: IOA Port=%d, Cascade=%d, Phy=%d\n",
+ path_active_desc[i].desc, path_state_desc[j].desc,
+ fabric->ioa_port, fabric->cascaded_expander, fabric->phy);
+ }
+ return;
+ }
+ }
+
+ ipr_err("Path state=%02X IOA Port=%d Cascade=%d Phy=%d\n", path_state,
+ fabric->ioa_port, fabric->cascaded_expander, fabric->phy);
+}
+
+static const struct {
+ u8 type;
+ char *desc;
+} path_type_desc[] = {
+ { IPR_PATH_CFG_IOA_PORT, "IOA port" },
+ { IPR_PATH_CFG_EXP_PORT, "Expander port" },
+ { IPR_PATH_CFG_DEVICE_PORT, "Device port" },
+ { IPR_PATH_CFG_DEVICE_LUN, "Device LUN" }
+};
+
+static const struct {
+ u8 status;
+ char *desc;
+} path_status_desc[] = {
+ { IPR_PATH_CFG_NO_PROB, "Functional" },
+ { IPR_PATH_CFG_DEGRADED, "Degraded" },
+ { IPR_PATH_CFG_FAILED, "Failed" },
+ { IPR_PATH_CFG_SUSPECT, "Suspect" },
+ { IPR_PATH_NOT_DETECTED, "Missing" },
+ { IPR_PATH_INCORRECT_CONN, "Incorrectly connected" }
+};
+
+static const char *link_rate[] = {
+ "unknown",
+ "disabled",
+ "phy reset problem",
+ "spinup hold",
+ "port selector",
+ "unknown",
+ "unknown",
+ "unknown",
+ "1.5Gbps",
+ "3.0Gbps",
+ "unknown",
+ "unknown",
+ "unknown",
+ "unknown",
+ "unknown",
+ "unknown"
+};
+
+/**
+ * ipr_log_path_elem - Log a fabric path element.
+ * @hostrcb: hostrcb struct
+ * @cfg: fabric path element struct
+ *
+ * Return value:
+ * none
+ **/
+static void ipr_log_path_elem(struct ipr_hostrcb *hostrcb,
+ struct ipr_hostrcb_config_element *cfg)
+{
+ int i, j;
+ u8 type = cfg->type_status & IPR_PATH_CFG_TYPE_MASK;
+ u8 status = cfg->type_status & IPR_PATH_CFG_STATUS_MASK;
+
+ if (type == IPR_PATH_CFG_NOT_EXIST)
+ return;
+
+ for (i = 0; i < ARRAY_SIZE(path_type_desc); i++) {
+ if (path_type_desc[i].type != type)
+ continue;
+
+ for (j = 0; j < ARRAY_SIZE(path_status_desc); j++) {
+ if (path_status_desc[j].status != status)
+ continue;
+
+ if (type == IPR_PATH_CFG_IOA_PORT) {
+ ipr_hcam_err(hostrcb, "%s %s: Phy=%d, Link rate=%s, WWN=%08X%08X\n",
+ path_status_desc[j].desc, path_type_desc[i].desc,
+ cfg->phy, link_rate[cfg->link_rate & IPR_PHY_LINK_RATE_MASK],
+ be32_to_cpu(cfg->wwid[0]), be32_to_cpu(cfg->wwid[1]));
+ } else {
+ if (cfg->cascaded_expander == 0xff && cfg->phy == 0xff) {
+ ipr_hcam_err(hostrcb, "%s %s: Link rate=%s, WWN=%08X%08X\n",
+ path_status_desc[j].desc, path_type_desc[i].desc,
+ link_rate[cfg->link_rate & IPR_PHY_LINK_RATE_MASK],
+ be32_to_cpu(cfg->wwid[0]), be32_to_cpu(cfg->wwid[1]));
+ } else if (cfg->cascaded_expander == 0xff) {
+ ipr_hcam_err(hostrcb, "%s %s: Phy=%d, Link rate=%s, "
+ "WWN=%08X%08X\n", path_status_desc[j].desc,
+ path_type_desc[i].desc, cfg->phy,
+ link_rate[cfg->link_rate & IPR_PHY_LINK_RATE_MASK],
+ be32_to_cpu(cfg->wwid[0]), be32_to_cpu(cfg->wwid[1]));
+ } else if (cfg->phy == 0xff) {
+ ipr_hcam_err(hostrcb, "%s %s: Cascade=%d, Link rate=%s, "
+ "WWN=%08X%08X\n", path_status_desc[j].desc,
+ path_type_desc[i].desc, cfg->cascaded_expander,
+ link_rate[cfg->link_rate & IPR_PHY_LINK_RATE_MASK],
+ be32_to_cpu(cfg->wwid[0]), be32_to_cpu(cfg->wwid[1]));
+ } else {
+ ipr_hcam_err(hostrcb, "%s %s: Cascade=%d, Phy=%d, Link rate=%s "
+ "WWN=%08X%08X\n", path_status_desc[j].desc,
+ path_type_desc[i].desc, cfg->cascaded_expander, cfg->phy,
+ link_rate[cfg->link_rate & IPR_PHY_LINK_RATE_MASK],
+ be32_to_cpu(cfg->wwid[0]), be32_to_cpu(cfg->wwid[1]));
+ }
+ }
+ return;
+ }
+ }
+
+ ipr_hcam_err(hostrcb, "Path element=%02X: Cascade=%d Phy=%d Link rate=%s "
+ "WWN=%08X%08X\n", cfg->type_status, cfg->cascaded_expander, cfg->phy,
+ link_rate[cfg->link_rate & IPR_PHY_LINK_RATE_MASK],
+ be32_to_cpu(cfg->wwid[0]), be32_to_cpu(cfg->wwid[1]));
+}
+
+/**
+ * ipr_log_fabric_error - Log a fabric error.
+ * @ioa_cfg: ioa config struct
+ * @hostrcb: hostrcb struct
+ *
+ * Return value:
+ * none
+ **/
+static void ipr_log_fabric_error(struct ipr_ioa_cfg *ioa_cfg,
+ struct ipr_hostrcb *hostrcb)
+{
+ struct ipr_hostrcb_type_20_error *error;
+ struct ipr_hostrcb_fabric_desc *fabric;
+ struct ipr_hostrcb_config_element *cfg;
+ int i, add_len;
+
+ error = &hostrcb->hcam.u.error.u.type_20_error;
+ error->failure_reason[sizeof(error->failure_reason) - 1] = '\0';
+ ipr_hcam_err(hostrcb, "%s\n", error->failure_reason);
+
+ add_len = be32_to_cpu(hostrcb->hcam.length) -
+ (offsetof(struct ipr_hostrcb_error, u) +
+ offsetof(struct ipr_hostrcb_type_20_error, desc));
+
+ for (i = 0, fabric = error->desc; i < error->num_entries; i++) {
+ ipr_log_fabric_path(hostrcb, fabric);
+ for_each_fabric_cfg(fabric, cfg)
+ ipr_log_path_elem(hostrcb, cfg);
+
+ add_len -= be16_to_cpu(fabric->length);
+ fabric = (struct ipr_hostrcb_fabric_desc *)
+ ((unsigned long)fabric + be16_to_cpu(fabric->length));
+ }
+
+ ipr_log_hex_data(ioa_cfg, (u32 *)fabric, add_len);
+}
+
/**
* ipr_log_generic_error - Log an adapter error.
* @ioa_cfg: ioa config struct
static void ipr_log_generic_error(struct ipr_ioa_cfg *ioa_cfg,
struct ipr_hostrcb *hostrcb)
{
- ipr_log_hex_data(hostrcb->hcam.u.raw.data,
+ ipr_log_hex_data(ioa_cfg, hostrcb->hcam.u.raw.data,
be32_to_cpu(hostrcb->hcam.length));
}
int i;
for (i = 0; i < ARRAY_SIZE(ipr_error_table); i++)
- if (ipr_error_table[i].ioasc == ioasc)
+ if (ipr_error_table[i].ioasc == (ioasc & IPR_IOASC_IOASC_MASK))
return i;
return 0;
if (!ipr_error_table[error_index].log_hcam)
return;
- if (ipr_is_device(&hostrcb->hcam.u.error.failing_dev_res_addr)) {
- ipr_res_err(ioa_cfg, hostrcb->hcam.u.error.failing_dev_res_addr,
- "%s\n", ipr_error_table[error_index].error);
- } else {
- dev_err(&ioa_cfg->pdev->dev, "%s\n",
- ipr_error_table[error_index].error);
- }
+ ipr_hcam_err(hostrcb, "%s\n", ipr_error_table[error_index].error);
/* Set indication we have logged an error */
ioa_cfg->errors_logged++;
case IPR_HOST_RCB_OVERLAY_ID_17:
ipr_log_enhanced_dual_ioa_error(ioa_cfg, hostrcb);
break;
+ case IPR_HOST_RCB_OVERLAY_ID_20:
+ ipr_log_fabric_error(ioa_cfg, hostrcb);
+ break;
case IPR_HOST_RCB_OVERLAY_ID_1:
case IPR_HOST_RCB_OVERLAY_ID_DEFAULT:
default:
/**
* ipr_worker_thread - Worker thread
- * @data: ioa config struct
+ * @work: ioa config struct
*
* Called at task level from a work thread. This function takes care
* of adding and removing device from the mid-layer as configuration
* Return value:
* nothing
**/
-static void ipr_worker_thread(void *data)
+static void ipr_worker_thread(struct work_struct *work)
{
unsigned long lock_flags;
struct ipr_resource_entry *res;
struct scsi_device *sdev;
struct ipr_dump *dump;
- struct ipr_ioa_cfg *ioa_cfg = data;
+ struct ipr_ioa_cfg *ioa_cfg =
+ container_of(work, struct ipr_ioa_cfg, work_q);
u8 bus, target, lun;
int did_work;
did_work = 1;
sdev = res->sdev;
if (!scsi_device_get(sdev)) {
- res->sdev = NULL;
list_move_tail(&res->queue, &ioa_cfg->free_res_q);
spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
scsi_remove_device(sdev);
bus = res->cfgte.res_addr.bus;
target = res->cfgte.res_addr.target;
lun = res->cfgte.res_addr.lun;
+ res->add_to_ml = 0;
spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
scsi_add_device(ioa_cfg->host, bus, target, lun);
spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
struct ipr_dump *dump;
unsigned long lock_flags = 0;
- ENTER;
dump = kzalloc(sizeof(struct ipr_dump), GFP_KERNEL);
if (!dump) {
}
spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
- LEAVE;
return 0;
}
**/
static int ipr_change_queue_depth(struct scsi_device *sdev, int qdepth)
{
+ struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *)sdev->host->hostdata;
+ struct ipr_resource_entry *res;
+ unsigned long lock_flags = 0;
+
+ spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
+ res = (struct ipr_resource_entry *)sdev->hostdata;
+
+ if (res && ipr_is_gata(res) && qdepth > IPR_MAX_CMD_PER_ATA_LUN)
+ qdepth = IPR_MAX_CMD_PER_ATA_LUN;
+ spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
+
scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), qdepth);
return sdev->queue_depth;
}
return 0;
}
+/**
+ * ipr_find_starget - Find target based on bus/target.
+ * @starget: scsi target struct
+ *
+ * Return value:
+ * resource entry pointer if found / NULL if not found
+ **/
+static struct ipr_resource_entry *ipr_find_starget(struct scsi_target *starget)
+{
+ struct Scsi_Host *shost = dev_to_shost(&starget->dev);
+ struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *) shost->hostdata;
+ struct ipr_resource_entry *res;
+
+ list_for_each_entry(res, &ioa_cfg->used_res_q, queue) {
+ if ((res->cfgte.res_addr.bus == starget->channel) &&
+ (res->cfgte.res_addr.target == starget->id) &&
+ (res->cfgte.res_addr.lun == 0)) {
+ return res;
+ }
+ }
+
+ return NULL;
+}
+
+static struct ata_port_info sata_port_info;
+
+/**
+ * ipr_target_alloc - Prepare for commands to a SCSI target
+ * @starget: scsi target struct
+ *
+ * If the device is a SATA device, this function allocates an
+ * ATA port with libata, else it does nothing.
+ *
+ * Return value:
+ * 0 on success / non-0 on failure
+ **/
+static int ipr_target_alloc(struct scsi_target *starget)
+{
+ struct Scsi_Host *shost = dev_to_shost(&starget->dev);
+ struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *) shost->hostdata;
+ struct ipr_sata_port *sata_port;
+ struct ata_port *ap;
+ struct ipr_resource_entry *res;
+ unsigned long lock_flags;
+
+ spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
+ res = ipr_find_starget(starget);
+ starget->hostdata = NULL;
+
+ if (res && ipr_is_gata(res)) {
+ spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
+ sata_port = kzalloc(sizeof(*sata_port), GFP_KERNEL);
+ if (!sata_port)
+ return -ENOMEM;
+
+ ap = ata_sas_port_alloc(&ioa_cfg->ata_host, &sata_port_info, shost);
+ if (ap) {
+ spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
+ sata_port->ioa_cfg = ioa_cfg;
+ sata_port->ap = ap;
+ sata_port->res = res;
+
+ res->sata_port = sata_port;
+ ap->private_data = sata_port;
+ starget->hostdata = sata_port;
+ } else {
+ kfree(sata_port);
+ return -ENOMEM;
+ }
+ }
+ spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
+
+ return 0;
+}
+
+/**
+ * ipr_target_destroy - Destroy a SCSI target
+ * @starget: scsi target struct
+ *
+ * If the device was a SATA device, this function frees the libata
+ * ATA port, else it does nothing.
+ *
+ **/
+static void ipr_target_destroy(struct scsi_target *starget)
+{
+ struct ipr_sata_port *sata_port = starget->hostdata;
+
+ if (sata_port) {
+ starget->hostdata = NULL;
+ ata_sas_port_destroy(sata_port->ap);
+ kfree(sata_port);
+ }
+}
+
+/**
+ * ipr_find_sdev - Find device based on bus/target/lun.
+ * @sdev: scsi device struct
+ *
+ * Return value:
+ * resource entry pointer if found / NULL if not found
+ **/
+static struct ipr_resource_entry *ipr_find_sdev(struct scsi_device *sdev)
+{
+ struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *) sdev->host->hostdata;
+ struct ipr_resource_entry *res;
+
+ list_for_each_entry(res, &ioa_cfg->used_res_q, queue) {
+ if ((res->cfgte.res_addr.bus == sdev->channel) &&
+ (res->cfgte.res_addr.target == sdev->id) &&
+ (res->cfgte.res_addr.lun == sdev->lun))
+ return res;
+ }
+
+ return NULL;
+}
+
/**
* ipr_slave_destroy - Unconfigure a SCSI device
* @sdev: scsi device struct
spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
res = (struct ipr_resource_entry *) sdev->hostdata;
if (res) {
+ if (res->sata_port)
+ ata_port_disable(res->sata_port->ap);
sdev->hostdata = NULL;
res->sdev = NULL;
+ res->sata_port = NULL;
}
spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
}
sdev->timeout = IPR_VSET_RW_TIMEOUT;
blk_queue_max_sectors(sdev->request_queue, IPR_VSET_MAX_SECTORS);
}
- if (IPR_IS_DASD_DEVICE(res->cfgte.std_inq_data))
+ if (ipr_is_vset_device(res) || ipr_is_scsi_disk(res))
sdev->allow_restart = 1;
- scsi_adjust_queue_depth(sdev, 0, sdev->host->cmd_per_lun);
+ if (ipr_is_gata(res) && res->sata_port) {
+ scsi_adjust_queue_depth(sdev, 0, IPR_MAX_CMD_PER_ATA_LUN);
+ ata_sas_slave_configure(sdev, res->sata_port->ap);
+ } else {
+ scsi_adjust_queue_depth(sdev, 0, sdev->host->cmd_per_lun);
+ }
}
spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
return 0;
}
+/**
+ * ipr_ata_slave_alloc - Prepare for commands to a SATA device
+ * @sdev: scsi device struct
+ *
+ * This function initializes an ATA port so that future commands
+ * sent through queuecommand will work.
+ *
+ * Return value:
+ * 0 on success
+ **/
+static int ipr_ata_slave_alloc(struct scsi_device *sdev)
+{
+ struct ipr_sata_port *sata_port = NULL;
+ int rc = -ENXIO;
+
+ ENTER;
+ if (sdev->sdev_target)
+ sata_port = sdev->sdev_target->hostdata;
+ if (sata_port)
+ rc = ata_sas_port_init(sata_port->ap);
+ if (rc)
+ ipr_slave_destroy(sdev);
+
+ LEAVE;
+ return rc;
+}
+
/**
* ipr_slave_alloc - Prepare for commands to a device.
* @sdev: scsi device struct
spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
- list_for_each_entry(res, &ioa_cfg->used_res_q, queue) {
- if ((res->cfgte.res_addr.bus == sdev->channel) &&
- (res->cfgte.res_addr.target == sdev->id) &&
- (res->cfgte.res_addr.lun == sdev->lun)) {
- res->sdev = sdev;
- res->add_to_ml = 0;
- res->in_erp = 0;
- sdev->hostdata = res;
- if (!ipr_is_naca_model(res))
- res->needs_sync_complete = 1;
- rc = 0;
- break;
+ res = ipr_find_sdev(sdev);
+ if (res) {
+ res->sdev = sdev;
+ res->add_to_ml = 0;
+ res->in_erp = 0;
+ sdev->hostdata = res;
+ if (!ipr_is_naca_model(res))
+ res->needs_sync_complete = 1;
+ rc = 0;
+ if (ipr_is_gata(res)) {
+ spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
+ return ipr_ata_slave_alloc(sdev);
}
}
return rc;
}
+/**
+ * ipr_device_reset - Reset the device
+ * @ioa_cfg: ioa config struct
+ * @res: resource entry struct
+ *
+ * This function issues a device reset to the affected device.
+ * If the device is a SCSI device, a LUN reset will be sent
+ * to the device first. If that does not work, a target reset
+ * will be sent. If the device is a SATA device, a PHY reset will
+ * be sent.
+ *
+ * Return value:
+ * 0 on success / non-zero on failure
+ **/
+static int ipr_device_reset(struct ipr_ioa_cfg *ioa_cfg,
+ struct ipr_resource_entry *res)
+{
+ struct ipr_cmnd *ipr_cmd;
+ struct ipr_ioarcb *ioarcb;
+ struct ipr_cmd_pkt *cmd_pkt;
+ struct ipr_ioarcb_ata_regs *regs;
+ u32 ioasc;
+
+ ENTER;
+ ipr_cmd = ipr_get_free_ipr_cmnd(ioa_cfg);
+ ioarcb = &ipr_cmd->ioarcb;
+ cmd_pkt = &ioarcb->cmd_pkt;
+ regs = &ioarcb->add_data.u.regs;
+
+ ioarcb->res_handle = res->cfgte.res_handle;
+ cmd_pkt->request_type = IPR_RQTYPE_IOACMD;
+ cmd_pkt->cdb[0] = IPR_RESET_DEVICE;
+ if (ipr_is_gata(res)) {
+ cmd_pkt->cdb[2] = IPR_ATA_PHY_RESET;
+ ioarcb->add_cmd_parms_len = cpu_to_be32(sizeof(regs->flags));
+ regs->flags |= IPR_ATA_FLAG_STATUS_ON_GOOD_COMPLETION;
+ }
+
+ ipr_send_blocking_cmd(ipr_cmd, ipr_timeout, IPR_DEVICE_RESET_TIMEOUT);
+ ioasc = be32_to_cpu(ipr_cmd->ioasa.ioasc);
+ list_add_tail(&ipr_cmd->queue, &ioa_cfg->free_q);
+ if (ipr_is_gata(res) && res->sata_port && ioasc != IPR_IOASC_IOA_WAS_RESET)
+ memcpy(&res->sata_port->ioasa, &ipr_cmd->ioasa.u.gata,
+ sizeof(struct ipr_ioasa_gata));
+
+ LEAVE;
+ return (IPR_IOASC_SENSE_KEY(ioasc) ? -EIO : 0);
+}
+
+/**
+ * ipr_sata_reset - Reset the SATA port
+ * @ap: SATA port to reset
+ * @classes: class of the attached device
+ *
+ * This function issues a SATA phy reset to the affected ATA port.
+ *
+ * Return value:
+ * 0 on success / non-zero on failure
+ **/
+static int ipr_sata_reset(struct ata_port *ap, unsigned int *classes)
+{
+ struct ipr_sata_port *sata_port = ap->private_data;
+ struct ipr_ioa_cfg *ioa_cfg = sata_port->ioa_cfg;
+ struct ipr_resource_entry *res;
+ unsigned long lock_flags = 0;
+ int rc = -ENXIO;
+
+ ENTER;
+ spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
+ while(ioa_cfg->in_reset_reload) {
+ spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
+ wait_event(ioa_cfg->reset_wait_q, !ioa_cfg->in_reset_reload);
+ spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
+ }
+
+ res = sata_port->res;
+ if (res) {
+ rc = ipr_device_reset(ioa_cfg, res);
+ switch(res->cfgte.proto) {
+ case IPR_PROTO_SATA:
+ case IPR_PROTO_SAS_STP:
+ *classes = ATA_DEV_ATA;
+ break;
+ case IPR_PROTO_SATA_ATAPI:
+ case IPR_PROTO_SAS_STP_ATAPI:
+ *classes = ATA_DEV_ATAPI;
+ break;
+ default:
+ *classes = ATA_DEV_UNKNOWN;
+ break;
+ };
+ }
+
+ spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
+ LEAVE;
+ return rc;
+}
+
/**
* ipr_eh_dev_reset - Reset the device
* @scsi_cmd: scsi command struct
struct ipr_cmnd *ipr_cmd;
struct ipr_ioa_cfg *ioa_cfg;
struct ipr_resource_entry *res;
- struct ipr_cmd_pkt *cmd_pkt;
- u32 ioasc;
+ struct ata_port *ap;
+ int rc = 0;
ENTER;
ioa_cfg = (struct ipr_ioa_cfg *) scsi_cmd->device->host->hostdata;
if (ipr_cmd->ioarcb.res_handle == res->cfgte.res_handle) {
if (ipr_cmd->scsi_cmd)
ipr_cmd->done = ipr_scsi_eh_done;
+ if (ipr_cmd->qc && !(ipr_cmd->qc->flags & ATA_QCFLAG_FAILED)) {
+ ipr_cmd->qc->err_mask |= AC_ERR_TIMEOUT;
+ ipr_cmd->qc->flags |= ATA_QCFLAG_FAILED;
+ }
}
}
res->resetting_device = 1;
+ scmd_printk(KERN_ERR, scsi_cmd, "Resetting device\n");
- ipr_cmd = ipr_get_free_ipr_cmnd(ioa_cfg);
-
- ipr_cmd->ioarcb.res_handle = res->cfgte.res_handle;
- cmd_pkt = &ipr_cmd->ioarcb.cmd_pkt;
- cmd_pkt->request_type = IPR_RQTYPE_IOACMD;
- cmd_pkt->cdb[0] = IPR_RESET_DEVICE;
-
- ipr_sdev_err(scsi_cmd->device, "Resetting device\n");
- ipr_send_blocking_cmd(ipr_cmd, ipr_timeout, IPR_DEVICE_RESET_TIMEOUT);
-
- ioasc = be32_to_cpu(ipr_cmd->ioasa.ioasc);
-
+ if (ipr_is_gata(res) && res->sata_port) {
+ ap = res->sata_port->ap;
+ spin_unlock_irq(scsi_cmd->device->host->host_lock);
+ ata_do_eh(ap, NULL, NULL, ipr_sata_reset, NULL);
+ spin_lock_irq(scsi_cmd->device->host->host_lock);
+ } else
+ rc = ipr_device_reset(ioa_cfg, res);
res->resetting_device = 0;
- list_add_tail(&ipr_cmd->queue, &ioa_cfg->free_q);
-
LEAVE;
- return (IPR_IOASC_SENSE_KEY(ioasc) ? FAILED : SUCCESS);
+ return (rc ? FAILED : SUCCESS);
}
static int ipr_eh_dev_reset(struct scsi_cmnd * cmd)
return;
}
- ipr_sdev_err(ipr_cmd->u.sdev, "Abort timed out. Resetting bus\n");
+ sdev_printk(KERN_ERR, ipr_cmd->u.sdev, "Abort timed out. Resetting bus.\n");
reset_cmd = ipr_get_free_ipr_cmnd(ioa_cfg);
ipr_cmd->sibling = reset_cmd;
reset_cmd->sibling = ipr_cmd;
*/
if (ioa_cfg->in_reset_reload || ioa_cfg->ioa_is_dead)
return FAILED;
- if (!res || (!ipr_is_gscsi(res) && !ipr_is_vset_device(res)))
+ if (!res || !ipr_is_gscsi(res))
return FAILED;
list_for_each_entry(ipr_cmd, &ioa_cfg->pending_q, queue) {
cmd_pkt->cdb[0] = IPR_CANCEL_ALL_REQUESTS;
ipr_cmd->u.sdev = scsi_cmd->device;
- ipr_sdev_err(scsi_cmd->device, "Aborting command: %02X\n", scsi_cmd->cmnd[0]);
+ scmd_printk(KERN_ERR, scsi_cmd, "Aborting command: %02X\n",
+ scsi_cmd->cmnd[0]);
ipr_send_blocking_cmd(ipr_cmd, ipr_abort_timeout, IPR_CANCEL_ALL_TIMEOUT);
ioasc = be32_to_cpu(ipr_cmd->ioasa.ioasc);
* ipr_isr - Interrupt service routine
* @irq: irq number
* @devp: pointer to ioa config struct
- * @regs: pt_regs struct
*
* Return value:
* IRQ_NONE / IRQ_HANDLED
**/
-static irqreturn_t ipr_isr(int irq, void *devp, struct pt_regs *regs)
+static irqreturn_t ipr_isr(int irq, void *devp)
{
struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *)devp;
unsigned long lock_flags = 0;
if (IPR_IOASC_SENSE_KEY(ioasc) > 0) {
scsi_cmd->result |= (DID_ERROR << 16);
- ipr_sdev_err(scsi_cmd->device,
- "Request Sense failed with IOASC: 0x%08X\n", ioasc);
+ scmd_printk(KERN_ERR, scsi_cmd,
+ "Request Sense failed with IOASC: 0x%08X\n", ioasc);
} else {
memcpy(scsi_cmd->sense_buffer, ipr_cmd->sense_buffer,
SCSI_SENSE_BUFFERSIZE);
* ipr_dump_ioasa - Dump contents of IOASA
* @ioa_cfg: ioa config struct
* @ipr_cmd: ipr command struct
+ * @res: resource entry struct
*
* This function is invoked by the interrupt handler when ops
* fail. It will log the IOASA if appropriate. Only called
* none
**/
static void ipr_dump_ioasa(struct ipr_ioa_cfg *ioa_cfg,
- struct ipr_cmnd *ipr_cmd)
+ struct ipr_cmnd *ipr_cmd, struct ipr_resource_entry *res)
{
int i;
u16 data_len;
return;
}
- ipr_sdev_err(ipr_cmd->scsi_cmd->device, "%s\n",
- ipr_error_table[error_index].error);
-
- if ((ioasa->u.gpdd.end_state <= ARRAY_SIZE(ipr_gpdd_dev_end_states)) &&
- (ioasa->u.gpdd.bus_phase <= ARRAY_SIZE(ipr_gpdd_dev_bus_phases))) {
- ipr_sdev_err(ipr_cmd->scsi_cmd->device,
- "Device End state: %s Phase: %s\n",
- ipr_gpdd_dev_end_states[ioasa->u.gpdd.end_state],
- ipr_gpdd_dev_bus_phases[ioasa->u.gpdd.bus_phase]);
- }
+ ipr_res_err(ioa_cfg, res, "%s\n", ipr_error_table[error_index].error);
if (sizeof(struct ipr_ioasa) < be16_to_cpu(ioasa->ret_stat_len))
data_len = sizeof(struct ipr_ioasa);
{
struct ipr_ioasa *ioasa = &ipr_cmd->ioasa;
- if ((be32_to_cpu(ioasa->ioasc_specific) &
- (IPR_ADDITIONAL_STATUS_FMT | IPR_AUTOSENSE_VALID)) == 0)
+ if ((be32_to_cpu(ioasa->ioasc_specific) & IPR_AUTOSENSE_VALID) == 0)
return 0;
memcpy(ipr_cmd->scsi_cmd->sense_buffer, ioasa->auto_sense.data,
}
if (ipr_is_gscsi(res))
- ipr_dump_ioasa(ioa_cfg, ipr_cmd);
+ ipr_dump_ioasa(ioa_cfg, ipr_cmd, res);
else
ipr_gen_sense(ipr_cmd);
case IPR_IOASC_NR_INIT_CMD_REQUIRED:
break;
default:
- scsi_cmd->result |= (DID_ERROR << 16);
+ if (IPR_IOASC_SENSE_KEY(ioasc) > RECOVERED_ERROR)
+ scsi_cmd->result |= (DID_ERROR << 16);
if (!ipr_is_vset_device(res) && !ipr_is_naca_model(res))
res->needs_sync_complete = 1;
break;
return 0;
}
+ if (ipr_is_gata(res) && res->sata_port)
+ return ata_sas_queuecmd(scsi_cmd, done, res->sata_port->ap);
+
ipr_cmd = ipr_get_free_ipr_cmnd(ioa_cfg);
ioarcb = &ipr_cmd->ioarcb;
list_add_tail(&ipr_cmd->queue, &ioa_cfg->pending_q);
return 0;
}
+/**
+ * ipr_ioctl - IOCTL handler
+ * @sdev: scsi device struct
+ * @cmd: IOCTL cmd
+ * @arg: IOCTL arg
+ *
+ * Return value:
+ * 0 on success / other on failure
+ **/
+static int ipr_ioctl(struct scsi_device *sdev, int cmd, void __user *arg)
+{
+ struct ipr_resource_entry *res;
+
+ res = (struct ipr_resource_entry *)sdev->hostdata;
+ if (res && ipr_is_gata(res))
+ return ata_scsi_ioctl(sdev, cmd, arg);
+
+ return -EINVAL;
+}
+
/**
* ipr_info - Get information about the card/driver
* @scsi_host: scsi host struct
.module = THIS_MODULE,
.name = "IPR",
.info = ipr_ioa_info,
+ .ioctl = ipr_ioctl,
.queuecommand = ipr_queuecommand,
.eh_abort_handler = ipr_eh_abort,
.eh_device_reset_handler = ipr_eh_dev_reset,
.slave_alloc = ipr_slave_alloc,
.slave_configure = ipr_slave_configure,
.slave_destroy = ipr_slave_destroy,
+ .target_alloc = ipr_target_alloc,
+ .target_destroy = ipr_target_destroy,
.change_queue_depth = ipr_change_queue_depth,
.change_queue_type = ipr_change_queue_type,
.bios_param = ipr_biosparam,
.proc_name = IPR_NAME
};
+/**
+ * ipr_ata_phy_reset - libata phy_reset handler
+ * @ap: ata port to reset
+ *
+ **/
+static void ipr_ata_phy_reset(struct ata_port *ap)
+{
+ unsigned long flags;
+ struct ipr_sata_port *sata_port = ap->private_data;
+ struct ipr_resource_entry *res = sata_port->res;
+ struct ipr_ioa_cfg *ioa_cfg = sata_port->ioa_cfg;
+ int rc;
+
+ ENTER;
+ spin_lock_irqsave(ioa_cfg->host->host_lock, flags);
+ while(ioa_cfg->in_reset_reload) {
+ spin_unlock_irqrestore(ioa_cfg->host->host_lock, flags);
+ wait_event(ioa_cfg->reset_wait_q, !ioa_cfg->in_reset_reload);
+ spin_lock_irqsave(ioa_cfg->host->host_lock, flags);
+ }
+
+ if (!ioa_cfg->allow_cmds)
+ goto out_unlock;
+
+ rc = ipr_device_reset(ioa_cfg, res);
+
+ if (rc) {
+ ap->ops->port_disable(ap);
+ goto out_unlock;
+ }
+
+ switch(res->cfgte.proto) {
+ case IPR_PROTO_SATA:
+ case IPR_PROTO_SAS_STP:
+ ap->device[0].class = ATA_DEV_ATA;
+ break;
+ case IPR_PROTO_SATA_ATAPI:
+ case IPR_PROTO_SAS_STP_ATAPI:
+ ap->device[0].class = ATA_DEV_ATAPI;
+ break;
+ default:
+ ap->device[0].class = ATA_DEV_UNKNOWN;
+ ap->ops->port_disable(ap);
+ break;
+ };
+
+out_unlock:
+ spin_unlock_irqrestore(ioa_cfg->host->host_lock, flags);
+ LEAVE;
+}
+
+/**
+ * ipr_ata_post_internal - Cleanup after an internal command
+ * @qc: ATA queued command
+ *
+ * Return value:
+ * none
+ **/
+static void ipr_ata_post_internal(struct ata_queued_cmd *qc)
+{
+ struct ipr_sata_port *sata_port = qc->ap->private_data;
+ struct ipr_ioa_cfg *ioa_cfg = sata_port->ioa_cfg;
+ struct ipr_cmnd *ipr_cmd;
+ unsigned long flags;
+
+ spin_lock_irqsave(ioa_cfg->host->host_lock, flags);
+ while(ioa_cfg->in_reset_reload) {
+ spin_unlock_irqrestore(ioa_cfg->host->host_lock, flags);
+ wait_event(ioa_cfg->reset_wait_q, !ioa_cfg->in_reset_reload);
+ spin_lock_irqsave(ioa_cfg->host->host_lock, flags);
+ }
+
+ list_for_each_entry(ipr_cmd, &ioa_cfg->pending_q, queue) {
+ if (ipr_cmd->qc == qc) {
+ ipr_device_reset(ioa_cfg, sata_port->res);
+ break;
+ }
+ }
+ spin_unlock_irqrestore(ioa_cfg->host->host_lock, flags);
+}
+
+/**
+ * ipr_tf_read - Read the current ATA taskfile for the ATA port
+ * @ap: ATA port
+ * @tf: destination ATA taskfile
+ *
+ * Return value:
+ * none
+ **/
+static void ipr_tf_read(struct ata_port *ap, struct ata_taskfile *tf)
+{
+ struct ipr_sata_port *sata_port = ap->private_data;
+ struct ipr_ioasa_gata *g = &sata_port->ioasa;
+
+ tf->feature = g->error;
+ tf->nsect = g->nsect;
+ tf->lbal = g->lbal;
+ tf->lbam = g->lbam;
+ tf->lbah = g->lbah;
+ tf->device = g->device;
+ tf->command = g->status;
+ tf->hob_nsect = g->hob_nsect;
+ tf->hob_lbal = g->hob_lbal;
+ tf->hob_lbam = g->hob_lbam;
+ tf->hob_lbah = g->hob_lbah;
+ tf->ctl = g->alt_status;
+}
+
+/**
+ * ipr_copy_sata_tf - Copy a SATA taskfile to an IOA data structure
+ * @regs: destination
+ * @tf: source ATA taskfile
+ *
+ * Return value:
+ * none
+ **/
+static void ipr_copy_sata_tf(struct ipr_ioarcb_ata_regs *regs,
+ struct ata_taskfile *tf)
+{
+ regs->feature = tf->feature;
+ regs->nsect = tf->nsect;
+ regs->lbal = tf->lbal;
+ regs->lbam = tf->lbam;
+ regs->lbah = tf->lbah;
+ regs->device = tf->device;
+ regs->command = tf->command;
+ regs->hob_feature = tf->hob_feature;
+ regs->hob_nsect = tf->hob_nsect;
+ regs->hob_lbal = tf->hob_lbal;
+ regs->hob_lbam = tf->hob_lbam;
+ regs->hob_lbah = tf->hob_lbah;
+ regs->ctl = tf->ctl;
+}
+
+/**
+ * ipr_sata_done - done function for SATA commands
+ * @ipr_cmd: ipr command struct
+ *
+ * This function is invoked by the interrupt handler for
+ * ops generated by the SCSI mid-layer to SATA devices
+ *
+ * Return value:
+ * none
+ **/
+static void ipr_sata_done(struct ipr_cmnd *ipr_cmd)
+{
+ struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
+ struct ata_queued_cmd *qc = ipr_cmd->qc;
+ struct ipr_sata_port *sata_port = qc->ap->private_data;
+ struct ipr_resource_entry *res = sata_port->res;
+ u32 ioasc = be32_to_cpu(ipr_cmd->ioasa.ioasc);
+
+ memcpy(&sata_port->ioasa, &ipr_cmd->ioasa.u.gata,
+ sizeof(struct ipr_ioasa_gata));
+ ipr_dump_ioasa(ioa_cfg, ipr_cmd, res);
+
+ if (be32_to_cpu(ipr_cmd->ioasa.ioasc_specific) & IPR_ATA_DEVICE_WAS_RESET)
+ scsi_report_device_reset(ioa_cfg->host, res->cfgte.res_addr.bus,
+ res->cfgte.res_addr.target);
+
+ if (IPR_IOASC_SENSE_KEY(ioasc) > RECOVERED_ERROR)
+ qc->err_mask |= __ac_err_mask(ipr_cmd->ioasa.u.gata.status);
+ else
+ qc->err_mask |= ac_err_mask(ipr_cmd->ioasa.u.gata.status);
+ list_add_tail(&ipr_cmd->queue, &ioa_cfg->free_q);
+ ata_qc_complete(qc);
+}
+
+/**
+ * ipr_build_ata_ioadl - Build an ATA scatter/gather list
+ * @ipr_cmd: ipr command struct
+ * @qc: ATA queued command
+ *
+ **/
+static void ipr_build_ata_ioadl(struct ipr_cmnd *ipr_cmd,
+ struct ata_queued_cmd *qc)
+{
+ u32 ioadl_flags = 0;
+ struct ipr_ioarcb *ioarcb = &ipr_cmd->ioarcb;
+ struct ipr_ioadl_desc *ioadl = ipr_cmd->ioadl;
+ int len = qc->nbytes + qc->pad_len;
+ struct scatterlist *sg;
+
+ if (len == 0)
+ return;
+
+ if (qc->dma_dir == DMA_TO_DEVICE) {
+ ioadl_flags = IPR_IOADL_FLAGS_WRITE;
+ ioarcb->cmd_pkt.flags_hi |= IPR_FLAGS_HI_WRITE_NOT_READ;
+ ioarcb->write_data_transfer_length = cpu_to_be32(len);
+ ioarcb->write_ioadl_len =
+ cpu_to_be32(sizeof(struct ipr_ioadl_desc) * ipr_cmd->dma_use_sg);
+ } else if (qc->dma_dir == DMA_FROM_DEVICE) {
+ ioadl_flags = IPR_IOADL_FLAGS_READ;
+ ioarcb->read_data_transfer_length = cpu_to_be32(len);
+ ioarcb->read_ioadl_len =
+ cpu_to_be32(sizeof(struct ipr_ioadl_desc) * ipr_cmd->dma_use_sg);
+ }
+
+ ata_for_each_sg(sg, qc) {
+ ioadl->flags_and_data_len = cpu_to_be32(ioadl_flags | sg_dma_len(sg));
+ ioadl->address = cpu_to_be32(sg_dma_address(sg));
+ if (ata_sg_is_last(sg, qc))
+ ioadl->flags_and_data_len |= cpu_to_be32(IPR_IOADL_FLAGS_LAST);
+ else
+ ioadl++;
+ }
+}
+
+/**
+ * ipr_qc_issue - Issue a SATA qc to a device
+ * @qc: queued command
+ *
+ * Return value:
+ * 0 if success
+ **/
+static unsigned int ipr_qc_issue(struct ata_queued_cmd *qc)
+{
+ struct ata_port *ap = qc->ap;
+ struct ipr_sata_port *sata_port = ap->private_data;
+ struct ipr_resource_entry *res = sata_port->res;
+ struct ipr_ioa_cfg *ioa_cfg = sata_port->ioa_cfg;
+ struct ipr_cmnd *ipr_cmd;
+ struct ipr_ioarcb *ioarcb;
+ struct ipr_ioarcb_ata_regs *regs;
+
+ if (unlikely(!ioa_cfg->allow_cmds || ioa_cfg->ioa_is_dead))
+ return -EIO;
+
+ ipr_cmd = ipr_get_free_ipr_cmnd(ioa_cfg);
+ ioarcb = &ipr_cmd->ioarcb;
+ regs = &ioarcb->add_data.u.regs;
+
+ memset(&ioarcb->add_data, 0, sizeof(ioarcb->add_data));
+ ioarcb->add_cmd_parms_len = cpu_to_be32(sizeof(ioarcb->add_data.u.regs));
+
+ list_add_tail(&ipr_cmd->queue, &ioa_cfg->pending_q);
+ ipr_cmd->qc = qc;
+ ipr_cmd->done = ipr_sata_done;
+ ipr_cmd->ioarcb.res_handle = res->cfgte.res_handle;
+ ioarcb->cmd_pkt.request_type = IPR_RQTYPE_ATA_PASSTHRU;
+ ioarcb->cmd_pkt.flags_hi |= IPR_FLAGS_HI_NO_LINK_DESC;
+ ioarcb->cmd_pkt.flags_hi |= IPR_FLAGS_HI_NO_ULEN_CHK;
+ ipr_cmd->dma_use_sg = qc->pad_len ? qc->n_elem + 1 : qc->n_elem;
+
+ ipr_build_ata_ioadl(ipr_cmd, qc);
+ regs->flags |= IPR_ATA_FLAG_STATUS_ON_GOOD_COMPLETION;
+ ipr_copy_sata_tf(regs, &qc->tf);
+ memcpy(ioarcb->cmd_pkt.cdb, qc->cdb, IPR_MAX_CDB_LEN);
+ ipr_trc_hook(ipr_cmd, IPR_TRACE_START, IPR_GET_PHYS_LOC(res->cfgte.res_addr));
+
+ switch (qc->tf.protocol) {
+ case ATA_PROT_NODATA:
+ case ATA_PROT_PIO:
+ break;
+
+ case ATA_PROT_DMA:
+ regs->flags |= IPR_ATA_FLAG_XFER_TYPE_DMA;
+ break;
+
+ case ATA_PROT_ATAPI:
+ case ATA_PROT_ATAPI_NODATA:
+ regs->flags |= IPR_ATA_FLAG_PACKET_CMD;
+ break;
+
+ case ATA_PROT_ATAPI_DMA:
+ regs->flags |= IPR_ATA_FLAG_PACKET_CMD;
+ regs->flags |= IPR_ATA_FLAG_XFER_TYPE_DMA;
+ break;
+
+ default:
+ WARN_ON(1);
+ return -1;
+ }
+
+ mb();
+ writel(be32_to_cpu(ioarcb->ioarcb_host_pci_addr),
+ ioa_cfg->regs.ioarrin_reg);
+ return 0;
+}
+
+/**
+ * ipr_ata_check_status - Return last ATA status
+ * @ap: ATA port
+ *
+ * Return value:
+ * ATA status
+ **/
+static u8 ipr_ata_check_status(struct ata_port *ap)
+{
+ struct ipr_sata_port *sata_port = ap->private_data;
+ return sata_port->ioasa.status;
+}
+
+/**
+ * ipr_ata_check_altstatus - Return last ATA altstatus
+ * @ap: ATA port
+ *
+ * Return value:
+ * Alt ATA status
+ **/
+static u8 ipr_ata_check_altstatus(struct ata_port *ap)
+{
+ struct ipr_sata_port *sata_port = ap->private_data;
+ return sata_port->ioasa.alt_status;
+}
+
+static struct ata_port_operations ipr_sata_ops = {
+ .port_disable = ata_port_disable,
+ .check_status = ipr_ata_check_status,
+ .check_altstatus = ipr_ata_check_altstatus,
+ .dev_select = ata_noop_dev_select,
+ .phy_reset = ipr_ata_phy_reset,
+ .post_internal_cmd = ipr_ata_post_internal,
+ .tf_read = ipr_tf_read,
+ .qc_prep = ata_noop_qc_prep,
+ .qc_issue = ipr_qc_issue,
+ .port_start = ata_sas_port_start,
+ .port_stop = ata_sas_port_stop
+};
+
+static struct ata_port_info sata_port_info = {
+ .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | ATA_FLAG_SATA_RESET |
+ ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA,
+ .pio_mask = 0x10, /* pio4 */
+ .mwdma_mask = 0x07,
+ .udma_mask = 0x7f, /* udma0-6 */
+ .port_ops = &ipr_sata_ops
+};
+
#ifdef CONFIG_PPC_PSERIES
static const u16 ipr_blocked_processors[] = {
PV_NORTHSTAR,
ipr_cmd->job_step = ipr_ioa_reset_done;
list_for_each_entry_continue(res, &ioa_cfg->used_res_q, queue) {
- if (!IPR_IS_DASD_DEVICE(res->cfgte.std_inq_data))
+ if (!ipr_is_scsi_disk(res))
continue;
ipr_cmd->u.res = res;
list_for_each_entry_safe(res, temp, &old_res, queue) {
if (res->sdev) {
res->del_from_ml = 1;
- res->sdev->hostdata = NULL;
+ res->cfgte.res_handle = IPR_INVALID_RES_HANDLE;
list_move_tail(&res->queue, &ioa_cfg->used_res_q);
} else {
list_move_tail(&res->queue, &ioa_cfg->free_res_q);
shutdown_type);
}
+/**
+ * ipr_reset_freeze - Hold off all I/O activity
+ * @ipr_cmd: ipr command struct
+ *
+ * Description: If the PCI slot is frozen, hold off all I/O
+ * activity; then, as soon as the slot is available again,
+ * initiate an adapter reset.
+ */
+static int ipr_reset_freeze(struct ipr_cmnd *ipr_cmd)
+{
+ /* Disallow new interrupts, avoid loop */
+ ipr_cmd->ioa_cfg->allow_interrupts = 0;
+ list_add_tail(&ipr_cmd->queue, &ipr_cmd->ioa_cfg->pending_q);
+ ipr_cmd->done = ipr_reset_ioa_job;
+ return IPR_RC_JOB_RETURN;
+}
+
+/**
+ * ipr_pci_frozen - Called when slot has experienced a PCI bus error.
+ * @pdev: PCI device struct
+ *
+ * Description: This routine is called to tell us that the PCI bus
+ * is down. Can't do anything here, except put the device driver
+ * into a holding pattern, waiting for the PCI bus to come back.
+ */
+static void ipr_pci_frozen(struct pci_dev *pdev)
+{
+ unsigned long flags = 0;
+ struct ipr_ioa_cfg *ioa_cfg = pci_get_drvdata(pdev);
+
+ spin_lock_irqsave(ioa_cfg->host->host_lock, flags);
+ _ipr_initiate_ioa_reset(ioa_cfg, ipr_reset_freeze, IPR_SHUTDOWN_NONE);
+ spin_unlock_irqrestore(ioa_cfg->host->host_lock, flags);
+}
+
+/**
+ * ipr_pci_slot_reset - Called when PCI slot has been reset.
+ * @pdev: PCI device struct
+ *
+ * Description: This routine is called by the pci error recovery
+ * code after the PCI slot has been reset, just before we
+ * should resume normal operations.
+ */
+static pci_ers_result_t ipr_pci_slot_reset(struct pci_dev *pdev)
+{
+ unsigned long flags = 0;
+ struct ipr_ioa_cfg *ioa_cfg = pci_get_drvdata(pdev);
+
+ spin_lock_irqsave(ioa_cfg->host->host_lock, flags);
+ _ipr_initiate_ioa_reset(ioa_cfg, ipr_reset_restore_cfg_space,
+ IPR_SHUTDOWN_NONE);
+ spin_unlock_irqrestore(ioa_cfg->host->host_lock, flags);
+ return PCI_ERS_RESULT_RECOVERED;
+}
+
+/**
+ * ipr_pci_perm_failure - Called when PCI slot is dead for good.
+ * @pdev: PCI device struct
+ *
+ * Description: This routine is called when the PCI bus has
+ * permanently failed.
+ */
+static void ipr_pci_perm_failure(struct pci_dev *pdev)
+{
+ unsigned long flags = 0;
+ struct ipr_ioa_cfg *ioa_cfg = pci_get_drvdata(pdev);
+
+ spin_lock_irqsave(ioa_cfg->host->host_lock, flags);
+ if (ioa_cfg->sdt_state == WAIT_FOR_DUMP)
+ ioa_cfg->sdt_state = ABORT_DUMP;
+ ioa_cfg->reset_retries = IPR_NUM_RESET_RELOAD_RETRIES;
+ ioa_cfg->in_ioa_bringdown = 1;
+ ipr_initiate_ioa_reset(ioa_cfg, IPR_SHUTDOWN_NONE);
+ spin_unlock_irqrestore(ioa_cfg->host->host_lock, flags);
+}
+
+/**
+ * ipr_pci_error_detected - Called when a PCI error is detected.
+ * @pdev: PCI device struct
+ * @state: PCI channel state
+ *
+ * Description: Called when a PCI error is detected.
+ *
+ * Return value:
+ * PCI_ERS_RESULT_NEED_RESET or PCI_ERS_RESULT_DISCONNECT
+ */
+static pci_ers_result_t ipr_pci_error_detected(struct pci_dev *pdev,
+ pci_channel_state_t state)
+{
+ switch (state) {
+ case pci_channel_io_frozen:
+ ipr_pci_frozen(pdev);
+ return PCI_ERS_RESULT_NEED_RESET;
+ case pci_channel_io_perm_failure:
+ ipr_pci_perm_failure(pdev);
+ return PCI_ERS_RESULT_DISCONNECT;
+ break;
+ default:
+ break;
+ }
+ return PCI_ERS_RESULT_NEED_RESET;
+}
+
/**
* ipr_probe_ioa_part2 - Initializes IOAs found in ipr_probe_ioa(..)
* @ioa_cfg: ioa cfg struct
return -ENOMEM;
for (i = 0; i < IPR_NUM_CMD_BLKS; i++) {
- ipr_cmd = pci_pool_alloc (ioa_cfg->ipr_cmd_pool, SLAB_KERNEL, &dma_addr);
+ ipr_cmd = pci_pool_alloc (ioa_cfg->ipr_cmd_pool, GFP_KERNEL, &dma_addr);
if (!ipr_cmd) {
ipr_free_cmd_blks(ioa_cfg);
ioa_cfg->hostrcb[i]->hostrcb_dma =
ioa_cfg->hostrcb_dma[i] + offsetof(struct ipr_hostrcb, hcam);
+ ioa_cfg->hostrcb[i]->ioa_cfg = ioa_cfg;
list_add_tail(&ioa_cfg->hostrcb[i]->queue, &ioa_cfg->hostrcb_free_q);
}
INIT_LIST_HEAD(&ioa_cfg->hostrcb_pending_q);
INIT_LIST_HEAD(&ioa_cfg->free_res_q);
INIT_LIST_HEAD(&ioa_cfg->used_res_q);
- INIT_WORK(&ioa_cfg->work_q, ipr_worker_thread, ioa_cfg);
+ INIT_WORK(&ioa_cfg->work_q, ipr_worker_thread);
init_waitqueue_head(&ioa_cfg->reset_wait_q);
ioa_cfg->sdt_state = INACTIVE;
if (ipr_enable_cache)
struct Scsi_Host *host;
unsigned long ipr_regs_pci;
void __iomem *ipr_regs;
- u32 rc = PCIBIOS_SUCCESSFUL;
+ int rc = PCIBIOS_SUCCESSFUL;
volatile u32 mask, uproc;
ENTER;
ioa_cfg = (struct ipr_ioa_cfg *)host->hostdata;
memset(ioa_cfg, 0, sizeof(struct ipr_ioa_cfg));
+ ata_host_init(&ioa_cfg->ata_host, &pdev->dev,
+ sata_port_info.flags, &ipr_sata_ops);
ioa_cfg->chip_cfg = ipr_get_chip_cfg(dev_id);
ioa_cfg->needs_hard_reset = 1;
ipr_mask_and_clear_interrupts(ioa_cfg, ~IPR_PCII_IOA_TRANS_TO_OPER);
- rc = request_irq(pdev->irq, ipr_isr, SA_SHIRQ, IPR_NAME, ioa_cfg);
+ rc = request_irq(pdev->irq, ipr_isr, IRQF_SHARED, IPR_NAME, ioa_cfg);
if (rc) {
dev_err(&pdev->dev, "Couldn't register IRQ %d! rc=%d\n",
{ PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_OBSIDIAN,
PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_572B,
0, 0, (kernel_ulong_t)&ipr_chip_cfg[0] },
+ { PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_OBSIDIAN,
+ PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_575C,
+ 0, 0, (kernel_ulong_t)&ipr_chip_cfg[0] },
{ PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_OBSIDIAN,
PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_572A,
0, 0, (kernel_ulong_t)&ipr_chip_cfg[0] },
{ PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_OBSIDIAN,
PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_572B,
0, 0, (kernel_ulong_t)&ipr_chip_cfg[0] },
+ { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_OBSIDIAN,
+ PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_575C,
+ 0, 0, (kernel_ulong_t)&ipr_chip_cfg[0] },
+ { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_OBSIDIAN,
+ PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_57B8,
+ 0, 0, (kernel_ulong_t)&ipr_chip_cfg[0] },
+ { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_OBSIDIAN_E,
+ PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_57B7,
+ 0, 0, (kernel_ulong_t)&ipr_chip_cfg[0] },
{ PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_SNIPE,
PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_2780,
0, 0, (kernel_ulong_t)&ipr_chip_cfg[1] },
{ PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_SCAMP,
PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_571F,
0, 0, (kernel_ulong_t)&ipr_chip_cfg[1] },
+ { PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_SCAMP,
+ PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_572F,
+ 0, 0, (kernel_ulong_t)&ipr_chip_cfg[1] },
{ }
};
MODULE_DEVICE_TABLE(pci, ipr_pci_table);
+static struct pci_error_handlers ipr_err_handler = {
+ .error_detected = ipr_pci_error_detected,
+ .slot_reset = ipr_pci_slot_reset,
+};
+
static struct pci_driver ipr_driver = {
.name = IPR_NAME,
.id_table = ipr_pci_table,
.probe = ipr_probe,
.remove = ipr_remove,
.shutdown = ipr_shutdown,
+ .err_handler = &ipr_err_handler,
};
/**
ipr_info("IBM Power RAID SCSI Device Driver version: %s %s\n",
IPR_DRIVER_VERSION, IPR_DRIVER_DATE);
- return pci_module_init(&ipr_driver);
+ return pci_register_driver(&ipr_driver);
}
/**