From 35bdddb83f62978b5fad82a14fbfd78cc3a5a60c Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Tue, 31 Jan 2006 00:13:33 -0500 Subject: sbp2: variable status FIFO address (fix login timeout) Let the ieee1394 core select a suitable 1394 address range for sbp2's status FIFO instead of using a fixed range. Since the core only selects addresses which are guaranteed to be out of the "physical range" as per OHCI 1.1, this patch also fixes an old bug: OHCI controllers which implement a writeable PhysicalUpperBound register included sbp2's status FIFO in the physical range. That way sbp2 was never notified of a succesful login and always failed after timeout. Affected OHCI host adapters include ALi and Fujitsu controllers. As another side effect of this patch, the status FIFO is no longer located in a range for which OHCI chips perform "posted writes". Each status write now requires a response subaction. But since large data transfers involve only few status writes, there is no measurable decrease of I/O throughput. What's more, the status FIFO is now safe from potential host bus errors. Nevertheless, posted writes could be re-enabled by extensions to the ARM features of the 1394 stack. Signed-off-by: Stefan Richter Signed-off-by: Jody McIntyre (cherry picked from b2d38cccad4ef80d6b672b8f89aae5fe2907b113 commit) --- drivers/ieee1394/sbp2.c | 64 +++++++++++++++++++++++++++---------------------- drivers/ieee1394/sbp2.h | 64 ++++++++++++++++++------------------------------- 2 files changed, 59 insertions(+), 69 deletions(-) diff --git a/drivers/ieee1394/sbp2.c b/drivers/ieee1394/sbp2.c index 8963dd484eb9d3..0672224fa109da 100644 --- a/drivers/ieee1394/sbp2.c +++ b/drivers/ieee1394/sbp2.c @@ -748,11 +748,6 @@ static struct scsi_id_instance_data *sbp2_alloc_device(struct unit_directory *ud hi->host = ud->ne->host; INIT_LIST_HEAD(&hi->scsi_ids); - /* Register our sbp2 status address space... */ - hpsb_register_addrspace(&sbp2_highlevel, ud->ne->host, &sbp2_ops, - SBP2_STATUS_FIFO_ADDRESS, - SBP2_STATUS_FIFO_ADDRESS + - SBP2_STATUS_FIFO_ENTRY_TO_OFFSET(SBP2_MAX_UDS_PER_NODE+1)); #ifdef CONFIG_IEEE1394_SBP2_PHYS_DMA /* Handle data movement if physical dma is not * enabled/supportedon host controller */ @@ -765,6 +760,18 @@ static struct scsi_id_instance_data *sbp2_alloc_device(struct unit_directory *ud list_add_tail(&scsi_id->scsi_list, &hi->scsi_ids); + /* Register the status FIFO address range. We could use the same FIFO + * for targets at different nodes. However we need different FIFOs per + * target in order to support multi-unit devices. */ + scsi_id->status_fifo_addr = hpsb_allocate_and_register_addrspace( + &sbp2_highlevel, ud->ne->host, &sbp2_ops, + sizeof(struct sbp2_status_block), sizeof(quadlet_t), + ~0ULL, ~0ULL); + if (!scsi_id->status_fifo_addr) { + SBP2_ERR("failed to allocate status FIFO address range"); + goto failed_alloc; + } + /* Register our host with the SCSI stack. */ scsi_host = scsi_host_alloc(&scsi_driver_template, sizeof(unsigned long)); @@ -1003,6 +1010,10 @@ static void sbp2_remove_device(struct scsi_id_instance_data *scsi_id) SBP2_DMA_FREE("single query logins data"); } + if (scsi_id->status_fifo_addr) + hpsb_unregister_addrspace(&sbp2_highlevel, hi->host, + scsi_id->status_fifo_addr); + scsi_id->ud->device.driver_data = NULL; SBP2_DEBUG("SBP-2 device removed, SCSI ID = %d", scsi_id->ud->id); @@ -1081,11 +1092,10 @@ static int sbp2_query_logins(struct scsi_id_instance_data *scsi_id) ORB_SET_QUERY_LOGINS_RESP_LENGTH(sizeof(struct sbp2_query_logins_response)); SBP2_DEBUG("sbp2_query_logins: reserved_resp_length initialized"); - scsi_id->query_logins_orb->status_FIFO_lo = SBP2_STATUS_FIFO_ADDRESS_LO + - SBP2_STATUS_FIFO_ENTRY_TO_OFFSET(scsi_id->ud->id); - scsi_id->query_logins_orb->status_FIFO_hi = (ORB_SET_NODE_ID(hi->host->node_id) | - SBP2_STATUS_FIFO_ADDRESS_HI); - SBP2_DEBUG("sbp2_query_logins: status FIFO initialized"); + scsi_id->query_logins_orb->status_fifo_hi = + ORB_SET_STATUS_FIFO_HI(scsi_id->status_fifo_addr, hi->host->node_id); + scsi_id->query_logins_orb->status_fifo_lo = + ORB_SET_STATUS_FIFO_LO(scsi_id->status_fifo_addr); sbp2util_cpu_to_be32_buffer(scsi_id->query_logins_orb, sizeof(struct sbp2_query_logins_orb)); @@ -1190,11 +1200,10 @@ static int sbp2_login_device(struct scsi_id_instance_data *scsi_id) ORB_SET_LOGIN_RESP_LENGTH(sizeof(struct sbp2_login_response)); SBP2_DEBUG("sbp2_login_device: passwd_resp_lengths initialized"); - scsi_id->login_orb->status_FIFO_lo = SBP2_STATUS_FIFO_ADDRESS_LO + - SBP2_STATUS_FIFO_ENTRY_TO_OFFSET(scsi_id->ud->id); - scsi_id->login_orb->status_FIFO_hi = (ORB_SET_NODE_ID(hi->host->node_id) | - SBP2_STATUS_FIFO_ADDRESS_HI); - SBP2_DEBUG("sbp2_login_device: status FIFO initialized"); + scsi_id->login_orb->status_fifo_hi = + ORB_SET_STATUS_FIFO_HI(scsi_id->status_fifo_addr, hi->host->node_id); + scsi_id->login_orb->status_fifo_lo = + ORB_SET_STATUS_FIFO_LO(scsi_id->status_fifo_addr); /* * Byte swap ORB if necessary @@ -1307,10 +1316,10 @@ static int sbp2_logout_device(struct scsi_id_instance_data *scsi_id) scsi_id->logout_orb->login_ID_misc |= ORB_SET_NOTIFY(1); scsi_id->logout_orb->reserved5 = 0x0; - scsi_id->logout_orb->status_FIFO_lo = SBP2_STATUS_FIFO_ADDRESS_LO + - SBP2_STATUS_FIFO_ENTRY_TO_OFFSET(scsi_id->ud->id); - scsi_id->logout_orb->status_FIFO_hi = (ORB_SET_NODE_ID(hi->host->node_id) | - SBP2_STATUS_FIFO_ADDRESS_HI); + scsi_id->logout_orb->status_fifo_hi = + ORB_SET_STATUS_FIFO_HI(scsi_id->status_fifo_addr, hi->host->node_id); + scsi_id->logout_orb->status_fifo_lo = + ORB_SET_STATUS_FIFO_LO(scsi_id->status_fifo_addr); /* * Byte swap ORB if necessary @@ -1372,10 +1381,10 @@ static int sbp2_reconnect_device(struct scsi_id_instance_data *scsi_id) scsi_id->reconnect_orb->login_ID_misc |= ORB_SET_NOTIFY(1); scsi_id->reconnect_orb->reserved5 = 0x0; - scsi_id->reconnect_orb->status_FIFO_lo = SBP2_STATUS_FIFO_ADDRESS_LO + - SBP2_STATUS_FIFO_ENTRY_TO_OFFSET(scsi_id->ud->id); - scsi_id->reconnect_orb->status_FIFO_hi = - (ORB_SET_NODE_ID(hi->host->node_id) | SBP2_STATUS_FIFO_ADDRESS_HI); + scsi_id->reconnect_orb->status_fifo_hi = + ORB_SET_STATUS_FIFO_HI(scsi_id->status_fifo_addr, hi->host->node_id); + scsi_id->reconnect_orb->status_fifo_lo = + ORB_SET_STATUS_FIFO_LO(scsi_id->status_fifo_addr); /* * Byte swap ORB if necessary @@ -2112,7 +2121,6 @@ static int sbp2_handle_status_write(struct hpsb_host *host, int nodeid, int dest { struct sbp2scsi_host_info *hi; struct scsi_id_instance_data *scsi_id = NULL, *scsi_id_tmp; - u32 id; struct scsi_cmnd *SCpnt = NULL; u32 scsi_status = SBP2_SCSI_STATUS_GOOD; struct sbp2_command_info *command; @@ -2135,12 +2143,12 @@ static int sbp2_handle_status_write(struct hpsb_host *host, int nodeid, int dest } /* - * Find our scsi_id structure by looking at the status fifo address written to by - * the sbp2 device. + * Find our scsi_id structure by looking at the status fifo address + * written to by the sbp2 device. */ - id = SBP2_STATUS_FIFO_OFFSET_TO_ENTRY((u32)(addr - SBP2_STATUS_FIFO_ADDRESS)); list_for_each_entry(scsi_id_tmp, &hi->scsi_ids, scsi_list) { - if (scsi_id_tmp->ne->nodeid == nodeid && scsi_id_tmp->ud->id == id) { + if (scsi_id_tmp->ne->nodeid == nodeid && + scsi_id_tmp->status_fifo_addr == addr) { scsi_id = scsi_id_tmp; break; } diff --git a/drivers/ieee1394/sbp2.h b/drivers/ieee1394/sbp2.h index 900ea1d25e7188..e2d357a9ea3a46 100644 --- a/drivers/ieee1394/sbp2.h +++ b/drivers/ieee1394/sbp2.h @@ -33,15 +33,17 @@ #define ORB_DIRECTION_NO_DATA_TRANSFER 0x2 #define ORB_SET_NULL_PTR(value) ((value & 0x1) << 31) -#define ORB_SET_NOTIFY(value) ((value & 0x1) << 31) -#define ORB_SET_RQ_FMT(value) ((value & 0x3) << 29) /* unused ? */ +#define ORB_SET_NOTIFY(value) ((value & 0x1) << 31) +#define ORB_SET_RQ_FMT(value) ((value & 0x3) << 29) /* unused ? */ #define ORB_SET_NODE_ID(value) ((value & 0xffff) << 16) -#define ORB_SET_DATA_SIZE(value) (value & 0xffff) -#define ORB_SET_PAGE_SIZE(value) ((value & 0x7) << 16) -#define ORB_SET_PAGE_TABLE_PRESENT(value) ((value & 0x1) << 19) -#define ORB_SET_MAX_PAYLOAD(value) ((value & 0xf) << 20) -#define ORB_SET_SPEED(value) ((value & 0x7) << 24) -#define ORB_SET_DIRECTION(value) ((value & 0x1) << 27) +#define ORB_SET_STATUS_FIFO_HI(value, id) (value >> 32 | ORB_SET_NODE_ID(id)) +#define ORB_SET_STATUS_FIFO_LO(value) (value & 0xffffffff) +#define ORB_SET_DATA_SIZE(value) (value & 0xffff) +#define ORB_SET_PAGE_SIZE(value) ((value & 0x7) << 16) +#define ORB_SET_PAGE_TABLE_PRESENT(value) ((value & 0x1) << 19) +#define ORB_SET_MAX_PAYLOAD(value) ((value & 0xf) << 20) +#define ORB_SET_SPEED(value) ((value & 0x7) << 24) +#define ORB_SET_DIRECTION(value) ((value & 0x1) << 27) struct sbp2_command_orb { volatile u32 next_ORB_hi; @@ -76,8 +78,8 @@ struct sbp2_login_orb { u32 login_response_lo; u32 lun_misc; u32 passwd_resp_lengths; - u32 status_FIFO_hi; - u32 status_FIFO_lo; + u32 status_fifo_hi; + u32 status_fifo_lo; }; #define RESPONSE_GET_LOGIN_ID(value) (value & 0xffff) @@ -102,8 +104,8 @@ struct sbp2_query_logins_orb { u32 query_response_lo; u32 lun_misc; u32 reserved_resp_length; - u32 status_FIFO_hi; - u32 status_FIFO_lo; + u32 status_fifo_hi; + u32 status_fifo_lo; }; #define RESPONSE_GET_MAX_LOGINS(value) (value & 0xffff) @@ -123,8 +125,8 @@ struct sbp2_reconnect_orb { u32 reserved4; u32 login_ID_misc; u32 reserved5; - u32 status_FIFO_hi; - u32 status_FIFO_lo; + u32 status_fifo_hi; + u32 status_fifo_lo; }; struct sbp2_logout_orb { @@ -134,8 +136,8 @@ struct sbp2_logout_orb { u32 reserved4; u32 login_ID_misc; u32 reserved5; - u32 status_FIFO_hi; - u32 status_FIFO_lo; + u32 status_fifo_hi; + u32 status_fifo_lo; }; #define PAGE_TABLE_SET_SEGMENT_BASE_HI(value) (value & 0xffff) @@ -195,30 +197,6 @@ struct sbp2_status_block { * Miscellaneous SBP2 related config rom defines */ -/* The status fifo address definition below is used as a base for each - * node, which a chunk seperately assigned to each unit directory in the - * node. For example, 0xfffe00000000ULL is used for the first sbp2 device - * detected on node 0, 0xfffe00000020ULL for the next sbp2 device on node - * 0, and so on. - * - * Note: We could use a single status fifo address for all sbp2 devices, - * and figure out which sbp2 device the status belongs to by looking at - * the source node id of the status write... but, using separate addresses - * for each sbp2 unit directory allows for better code and the ability to - * support multiple luns within a single 1394 node. - * - * Also note that we choose the address range below as it is a region - * specified for write posting, where the ohci controller will - * automatically send an ack_complete when the status is written by the - * sbp2 device... saving a split transaction. =) - */ -#define SBP2_STATUS_FIFO_ADDRESS 0xfffe00000000ULL -#define SBP2_STATUS_FIFO_ADDRESS_HI 0xfffe -#define SBP2_STATUS_FIFO_ADDRESS_LO 0x0 - -#define SBP2_STATUS_FIFO_ENTRY_TO_OFFSET(entry) ((entry) << 5) -#define SBP2_STATUS_FIFO_OFFSET_TO_ENTRY(offset) ((offset) >> 5) - #define SBP2_UNIT_DIRECTORY_OFFSET_KEY 0xd1 #define SBP2_CSR_OFFSET_KEY 0x54 #define SBP2_UNIT_SPEC_ID_KEY 0x12 @@ -258,7 +236,6 @@ struct sbp2_status_block { */ #define SBP2_MAX_SG_ELEMENT_LENGTH 0xf000 -#define SBP2_MAX_UDS_PER_NODE 16 /* Maximum scsi devices per node */ #define SBP2_MAX_SECTORS 255 /* Max sectors supported */ #define SBP2_MAX_CMDS 8 /* This should be safe */ @@ -337,6 +314,11 @@ struct scsi_id_instance_data { u32 sbp2_lun; u32 sbp2_firmware_revision; + /* + * Address for the device to write status blocks to + */ + u64 status_fifo_addr; + /* * Variable used for logins, reconnects, logouts, query logins */ -- cgit 1.2.3-korg