diff options
author | Anand Kumar Santhanam <AnandKumar.Santhanam@pmcs.com> | 2013-09-18 11:14:54 +0530 |
---|---|---|
committer | James Bottomley <JBottomley@Parallels.com> | 2013-10-25 09:58:15 +0100 |
commit | 0ecdf00ba6e50eaa452356d0d4e79657dab702a4 (patch) | |
tree | cef681a9f28e7bc82314ddf7fe6d81f9c9e1c022 /drivers | |
parent | f9cd6cbd80d1d5963ffd0eab3970235cd4205984 (diff) |
[SCSI] pm80xx: 4G boundary fix.
Firmware is having an issue. When a single IO request crosses
4G boundary, system will crash. To avoid the issue single sg is
converted into extended sg.
Signed-off-by: Anandkumar.Santhanam@pmcs.com
Reviewed-by: Jack Wang <jinpu.wang@profitbricks.com>
Signed-off-by: James Bottomley <JBottomley@Parallels.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/scsi/pm8001/pm80xx_hwi.c | 103 |
1 files changed, 101 insertions, 2 deletions
diff --git a/drivers/scsi/pm8001/pm80xx_hwi.c b/drivers/scsi/pm8001/pm80xx_hwi.c index fa06f24e1656..37ddc5dda7bb 100644 --- a/drivers/scsi/pm8001/pm80xx_hwi.c +++ b/drivers/scsi/pm8001/pm80xx_hwi.c @@ -3610,7 +3610,8 @@ static int pm80xx_chip_ssp_io_req(struct pm8001_hba_info *pm8001_ha, struct ssp_ini_io_start_req ssp_cmd; u32 tag = ccb->ccb_tag; int ret; - u64 phys_addr; + u64 phys_addr, start_addr, end_addr; + u32 end_addr_high, end_addr_low; struct inbound_queue_table *circularQ; u32 q_index; u32 opc = OPC_INB_SSPINIIOSTART; @@ -3664,6 +3665,30 @@ static int pm80xx_chip_ssp_io_req(struct pm8001_hba_info *pm8001_ha, cpu_to_le32(upper_32_bits(dma_addr)); ssp_cmd.enc_len = cpu_to_le32(task->total_xfer_len); ssp_cmd.enc_esgl = 0; + /* Check 4G Boundary */ + start_addr = cpu_to_le64(dma_addr); + end_addr = (start_addr + ssp_cmd.enc_len) - 1; + end_addr_low = cpu_to_le32(lower_32_bits(end_addr)); + end_addr_high = cpu_to_le32(upper_32_bits(end_addr)); + if (end_addr_high != ssp_cmd.enc_addr_high) { + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk("The sg list address " + "start_addr=0x%016llx data_len=0x%x " + "end_addr_high=0x%08x end_addr_low=" + "0x%08x has crossed 4G boundary\n", + start_addr, ssp_cmd.enc_len, + end_addr_high, end_addr_low)); + pm8001_chip_make_sg(task->scatter, 1, + ccb->buf_prd); + phys_addr = ccb->ccb_dma_handle + + offsetof(struct pm8001_ccb_info, + buf_prd[0]); + ssp_cmd.enc_addr_low = + cpu_to_le32(lower_32_bits(phys_addr)); + ssp_cmd.enc_addr_high = + cpu_to_le32(upper_32_bits(phys_addr)); + ssp_cmd.enc_esgl = cpu_to_le32(1<<31); + } } else if (task->num_scatter == 0) { ssp_cmd.enc_addr_low = 0; ssp_cmd.enc_addr_high = 0; @@ -3699,6 +3724,30 @@ static int pm80xx_chip_ssp_io_req(struct pm8001_hba_info *pm8001_ha, cpu_to_le32(upper_32_bits(dma_addr)); ssp_cmd.len = cpu_to_le32(task->total_xfer_len); ssp_cmd.esgl = 0; + /* Check 4G Boundary */ + start_addr = cpu_to_le64(dma_addr); + end_addr = (start_addr + ssp_cmd.len) - 1; + end_addr_low = cpu_to_le32(lower_32_bits(end_addr)); + end_addr_high = cpu_to_le32(upper_32_bits(end_addr)); + if (end_addr_high != ssp_cmd.addr_high) { + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk("The sg list address " + "start_addr=0x%016llx data_len=0x%x " + "end_addr_high=0x%08x end_addr_low=" + "0x%08x has crossed 4G boundary\n", + start_addr, ssp_cmd.len, + end_addr_high, end_addr_low)); + pm8001_chip_make_sg(task->scatter, 1, + ccb->buf_prd); + phys_addr = ccb->ccb_dma_handle + + offsetof(struct pm8001_ccb_info, + buf_prd[0]); + ssp_cmd.addr_low = + cpu_to_le32(lower_32_bits(phys_addr)); + ssp_cmd.addr_high = + cpu_to_le32(upper_32_bits(phys_addr)); + ssp_cmd.esgl = cpu_to_le32(1<<31); + } } else if (task->num_scatter == 0) { ssp_cmd.addr_low = 0; ssp_cmd.addr_high = 0; @@ -3723,7 +3772,8 @@ static int pm80xx_chip_sata_req(struct pm8001_hba_info *pm8001_ha, u32 q_index; struct sata_start_req sata_cmd; u32 hdr_tag, ncg_tag = 0; - u64 phys_addr; + u64 phys_addr, start_addr, end_addr; + u32 end_addr_high, end_addr_low; u32 ATAP = 0x0; u32 dir; struct inbound_queue_table *circularQ; @@ -3792,6 +3842,31 @@ static int pm80xx_chip_sata_req(struct pm8001_hba_info *pm8001_ha, sata_cmd.enc_addr_high = upper_32_bits(dma_addr); sata_cmd.enc_len = cpu_to_le32(task->total_xfer_len); sata_cmd.enc_esgl = 0; + /* Check 4G Boundary */ + start_addr = cpu_to_le64(dma_addr); + end_addr = (start_addr + sata_cmd.enc_len) - 1; + end_addr_low = cpu_to_le32(lower_32_bits(end_addr)); + end_addr_high = cpu_to_le32(upper_32_bits(end_addr)); + if (end_addr_high != sata_cmd.enc_addr_high) { + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk("The sg list address " + "start_addr=0x%016llx data_len=0x%x " + "end_addr_high=0x%08x end_addr_low" + "=0x%08x has crossed 4G boundary\n", + start_addr, sata_cmd.enc_len, + end_addr_high, end_addr_low)); + pm8001_chip_make_sg(task->scatter, 1, + ccb->buf_prd); + phys_addr = ccb->ccb_dma_handle + + offsetof(struct pm8001_ccb_info, + buf_prd[0]); + sata_cmd.enc_addr_low = + lower_32_bits(phys_addr); + sata_cmd.enc_addr_high = + upper_32_bits(phys_addr); + sata_cmd.enc_esgl = + cpu_to_le32(1 << 31); + } } else if (task->num_scatter == 0) { sata_cmd.enc_addr_low = 0; sata_cmd.enc_addr_high = 0; @@ -3833,6 +3908,30 @@ static int pm80xx_chip_sata_req(struct pm8001_hba_info *pm8001_ha, sata_cmd.addr_high = upper_32_bits(dma_addr); sata_cmd.len = cpu_to_le32(task->total_xfer_len); sata_cmd.esgl = 0; + /* Check 4G Boundary */ + start_addr = cpu_to_le64(dma_addr); + end_addr = (start_addr + sata_cmd.len) - 1; + end_addr_low = cpu_to_le32(lower_32_bits(end_addr)); + end_addr_high = cpu_to_le32(upper_32_bits(end_addr)); + if (end_addr_high != sata_cmd.addr_high) { + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk("The sg list address " + "start_addr=0x%016llx data_len=0x%x" + "end_addr_high=0x%08x end_addr_low=" + "0x%08x has crossed 4G boundary\n", + start_addr, sata_cmd.len, + end_addr_high, end_addr_low)); + pm8001_chip_make_sg(task->scatter, 1, + ccb->buf_prd); + phys_addr = ccb->ccb_dma_handle + + offsetof(struct pm8001_ccb_info, + buf_prd[0]); + sata_cmd.addr_low = + lower_32_bits(phys_addr); + sata_cmd.addr_high = + upper_32_bits(phys_addr); + sata_cmd.esgl = cpu_to_le32(1 << 31); + } } else if (task->num_scatter == 0) { sata_cmd.addr_low = 0; sata_cmd.addr_high = 0; |