From mboxrd@z Thu Jan 1 00:00:00 1970 From: Mark Salyzyn Subject: [PATCH] pm8001: support HDA (flashless) mode Date: Fri, 27 Apr 2012 13:10:57 -0400 Message-ID: <420864BE-8542-4BD8-B3DB-FDA403D193F9@xyratex.com> Mime-Version: 1.0 (Apple Message framework v1257) Content-Type: multipart/mixed; boundary="Apple-Mail=_12FA110C-D8A2-412B-9446-7E8B9E6F348A" Return-path: Received: from xyratex198.xyratex.com ([194.131.166.198]:38828 "EHLO XY01EX22.xy01.xyratex.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1760820Ab2D0RLE (ORCPT ); Fri, 27 Apr 2012 13:11:04 -0400 Sender: linux-scsi-owner@vger.kernel.org List-Id: linux-scsi@vger.kernel.org To: linux-scsi@vger.kernel.org Cc: Mark Salyzyn , Jack Wang , James Bottomley , lindar_liu , =?utf-8?B?5LqO54ix5Y2O?= , john_gong --Apple-Mail=_12FA110C-D8A2-412B-9446-7E8B9E6F348A Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset=us-ascii The pm8001 can be delivered as a standalone product with = flash-programmed firmware images, or without the flash present requiring the driver to = upload the images into the chip's RAM and then run. This is called HDA mode. We add support for this firmware upload in the enclosed patch. We try = some basic initialization checks of the Firmware, and if it appears dead, we = make the assumption the adapter must in-fact be halted in this HDA mode. The Firmware images themselves have not been cleared for open-release by = PMC, but they are available in OpenSolaris . PMC's rationalization for = not wanting an open-release of the Firmware Images is that they do not want = to take support calls except from paying OEMs (such as Xyratex) that are = embedding PMC product into the motherboards and thus may have a tested combination = of Firmware and Hardware. Please respect this sentiment. Images are = expected in: /lib/firmware/aap1img.bin /lib/firmware/ilaimg.bin /lib/firmware/iopimg.bin /lib/firmware/istrimg.bin using the exact same naming convention as PMC and in OpenSolaris (and = its followon children) for these image files. Signed-off-by: Mark Salyzyn drivers/scsi/pm8001/pm8001_hwi.c | 584 = +++++++++++++++++++++++++++++++++++--- drivers/scsi/pm8001/pm8001_hwi.h | 37 ++ drivers/scsi/pm8001/pm8001_init.c | 30 + drivers/scsi/pm8001/pm8001_sas.h | 3=20 4 files changed, 613 insertions(+), 41 deletions(-) Please see enclosed attachment --Apple-Mail=_12FA110C-D8A2-412B-9446-7E8B9E6F348A Content-Disposition: attachment; filename=pm8001-hda.patch Content-Type: application/octet-stream; name="pm8001-hda.patch" Content-Transfer-Encoding: 7bit The pm8001 can be delivered as a standalone product with flash-programmed firmware images, or without the flash present requiring the driver to upload the images into the chip's RAM and then run. This is called HDA mode. We add support for this firmware upload in the enclosed patch. We try some basic initialization checks of the Firmware, and if it appears dead, we make the assumption the adapter must in-fact be halted in this HDA mode. The Firmware images themselves have not been cleared for open-release by PMC, but they are available in OpenSolaris . PMC's rationalization for not wanting an open-release of the Firmware Images is that they do not want to take support calls except from paying OEMs (such as Xyratex) that are embedding PMC product into the motherboards and thus may have a tested combination of Firmware and Hardware. Please respect this sentiment. Images are expected in: /lib/firmware/aap1img.bin /lib/firmware/ilaimg.bin /lib/firmware/iopimg.bin /lib/firmware/istrimg.bin using the exact same naming convention as PMC and in OpenSolaris (and its followon children) for these image files. Signed-off-by: Mark Salyzyn drivers/scsi/pm8001/pm8001_hwi.c | 584 +++++++++++++++++++++++++++++++++++--- drivers/scsi/pm8001/pm8001_hwi.h | 37 ++ drivers/scsi/pm8001/pm8001_init.c | 30 + drivers/scsi/pm8001/pm8001_sas.h | 3 4 files changed, 613 insertions(+), 41 deletions(-) diff --git a/drivers/scsi/pm8001/pm8001_hwi.c b/drivers/scsi/pm8001/pm8001_hwi.c index 8477df4..25c733c 100644 --- a/drivers/scsi/pm8001/pm8001_hwi.c +++ b/drivers/scsi/pm8001/pm8001_hwi.c @@ -37,11 +37,13 @@ * POSSIBILITY OF SUCH DAMAGES. * */ - #include - #include "pm8001_sas.h" - #include "pm8001_hwi.h" - #include "pm8001_chips.h" - #include "pm8001_ctl.h" +#include +#include "pm8001_sas.h" +#include "pm8001_hwi.h" +#include "pm8001_chips.h" +#include "pm8001_ctl.h" + +#include /** * read_main_config_table - read the configure table and save it. @@ -63,6 +65,9 @@ static void __devinit read_main_config_table(struct pm8001_hba_info *pm8001_ha) pm8001_mr32(address, MAIN_OBQ_OFFSET); pm8001_ha->main_cfg_tbl.hda_mode_flag = pm8001_mr32(address, MAIN_HDA_FLAGS_OFFSET); + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("MAIN_HDA_FLAGS = 0x%x\n", + pm8001_ha->main_cfg_tbl.hda_mode_flag)); /* read analog Setting offset from the configuration table */ pm8001_ha->main_cfg_tbl.anolog_setup_table_offset = @@ -597,6 +602,484 @@ static void init_pci_device_addresses(struct pm8001_hba_info *pm8001_ha) base_addr + pm8001_cr32(pm8001_ha, pcibar, offset + 0x20); } +static void pm8001_hda_send_cmd(struct pm8001_hba_info *pm8001_ha, + u32 arg_array[], u32 num_args, u32 cmd) +{ + u32 reg; + u32 i; + + for (i = 0; i < num_args; i++) + pm8001_cw32(pm8001_ha, 3, HDA_CMD_OFFSET+(i*4), arg_array[i]); + + reg = pm8001_cr32(pm8001_ha, 3, HDA_CMD_OFFSET+28); + reg = (reg & HDA_SEQ_ID_BITS) >> 16; + if (reg == 0xff) + reg = 0; + reg++; + reg = ((HDA_C_PA << 24) | (reg << 16) | cmd); + pm8001_cw32(pm8001_ha, 3, HDA_CMD_OFFSET+28, reg); +} + +static u32 pm8001_hda_recv_rsp(struct pm8001_hba_info *pm8001_ha, u32 cmd) +{ + u32 rsp; + u32 i = 0; + + do { + mdelay(10); + rsp = pm8001_cr32(pm8001_ha, 3, HDA_CMD_OFFSET+28); + rsp = rsp & HDA_CODE_BITS; + + switch (cmd) { + case HDAC_CMD_EXEC: + if (rsp == HDA_RSP_EXEC) + return 1; + if (rsp == HDA_RSP_BAD_IMG) + return 0; + if (rsp == HDA_RSP_BAD_CMD) + return 0; + break; + } + i++; + } while (i < 200); + + return 1; +} + +static u32 pm8001_bar4_cpy(struct pm8001_hba_info *pm8001_ha, + u32 base, u32 offset, const unsigned char array[], u32 alen) +{ + u32 dbase; + u32 doffset; + u32 *larray; + u32 val; + u32 csize; + u32 wc; + u32 i = 0; + unsigned long flags; + + PM8001_INIT_DBG(pm8001_ha, pm8001_printk("alen = 0x%x\n", alen)); + + dbase = (base+offset) & MB3_SHIFT_MASK; + doffset = offset & MB3_OFFSET_MASK; + spin_lock_irqsave(&pm8001_ha->lock, flags); + do { + if (-1 == pm8001_bar4_shift(pm8001_ha, dbase)) { + spin_unlock_irqrestore(&pm8001_ha->lock, flags); + return 0; + } + + if ((doffset+alen) > SIZE_64KB) + csize = SIZE_64KB - doffset; + else + csize = alen; + + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("ILA STR dbase = 0x%x\n", dbase)); + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("ILA STR doffset = 0x%x\n", doffset)); + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("ILA STR size = 0x%x\n", csize)); + + wc = ((csize % 4) > 0) ? ((csize / 4) + 1) : (csize / 4); + larray = (u32 *)array; + for (i = 0; i < wc; i++) { + val = larray[i]; + pm8001_cw32(pm8001_ha, 2, (doffset + (i*4)), val); + } + + alen -= csize; + dbase += SIZE_64KB; + doffset = 0; + array = array + csize; + } while (alen != 0); + + if (-1 == pm8001_bar4_shift(pm8001_ha, 0x0)) { + spin_unlock_irqrestore(&pm8001_ha->lock, flags); + return 0; + } + spin_unlock_irqrestore(&pm8001_ha->lock, flags); + + return 1; +} + +static u32 pm8001_bar4_cpy_big(struct pm8001_hba_info *pm8001_ha, + u32 base, u32 offset, const unsigned char array[], u32 alen) +{ + u32 dbase; + u32 doffset; + u32 *larray; + u32 val; + u32 csize; + u32 wc; + u32 i = 0; + u8 *local_buffer = NULL; + unsigned long flags; + + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("alen = 0x%x\n", alen)); + + dbase = (base+offset) & MB3_SHIFT_MASK; + doffset = offset & MB3_OFFSET_MASK; + spin_lock_irqsave(&pm8001_ha->lock, flags); + do { + if (-1 == pm8001_bar4_shift(pm8001_ha, dbase)) { + spin_unlock_irqrestore(&pm8001_ha->lock, flags); + return 0; + } + + if ((doffset+alen) > SIZE_64KB) + csize = SIZE_64KB - doffset; + else + csize = alen; + + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("ILA STR dbase = 0x%x\n", dbase)); + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("ILA STR doffset = 0x%x\n", doffset)); + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("ILA STR size = 0x%x\n", csize)); + + wc = ((csize % 4) > 0) ? ((csize / 4) + 1) : (csize / 4); + local_buffer = kmalloc(csize, GFP_KERNEL); + if (local_buffer != NULL) + memcpy(local_buffer, array, csize); + larray = (u32 *)local_buffer; + for (i = 0; i < wc; i++) { + val = larray[i]; + pm8001_cw32(pm8001_ha, 2, (doffset + (i*4)), val); + } + kfree(local_buffer); + alen -= csize; + dbase += SIZE_64KB; + doffset = 0; + array = array + csize; + } while (alen != 0); + + if (-1 == pm8001_bar4_shift(pm8001_ha, 0x0)) { + spin_unlock_irqrestore(&pm8001_ha->lock, flags); + return 0; + } + spin_unlock_irqrestore(&pm8001_ha->lock, flags); + + return 1; +} + + +static int pm8001_ishdar_idle(struct pm8001_hba_info *pm8001_ha) +{ + u32 hdaw; + u32 pcilogic; + + pcilogic = get_pci_bar_index(0x24); + + hdaw = pm8001_cr32(pm8001_ha, pcilogic, HDA_RSP_OFFSET+28); + if ((((hdaw & HDA_PA_BITS) >> 24) == HDA_R_PA) + && ((hdaw & HDA_CODE_BITS) == HDA_RSP_IDLE)) + return 1; + + return 0; +} + +static int mpi_uninit_check(struct pm8001_hba_info *pm8001_ha); + +/* Catch-22, this is called before chip initialization, values may change */ +static int __devinit pm8001_chip_in_hda_mode(struct pm8001_hba_info *pm8001_ha) +{ + u32 data; + + if (!pm8001_ha->main_cfg_tbl_addr) + init_pci_device_addresses(pm8001_ha); + if (!pm8001_ha->main_cfg_tbl_addr) + return 0; + if (mpi_uninit_check(pm8001_ha) != 0) { + /* Must be sick, must be in HDA mode? */ + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("MPI state not ready, in HDA mode?\n")); + return 1; + } + /* check the firmware status */ + if (-1 == check_fw_ready(pm8001_ha)) { + /* Must be sick, must be in HDA mode? */ + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("Firmware is not ready, in HDA mode?\n")); + return 1; + } + if (!pm8001_ha->main_cfg_tbl.hda_mode_flag) + read_main_config_table(pm8001_ha); + if (!pm8001_ha->main_cfg_tbl.hda_mode_flag) { + /* Must be sick, must be in HDA mode? */ + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("HDA mode offset bad, in HDA mode?\n")); + return 1; + } + data = pm8001_mr32( + pm8001_ha->main_cfg_tbl_addr, + pm8001_ha->main_cfg_tbl.hda_mode_flag); + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("*MAIN_HDA_FLAGS = 0x%x\n", data)); + return data & 0x4; +} + +static int +pm8001_chip_soft_rst(struct pm8001_hba_info *pm8001_ha, u32 signature); + +static int pm8001_chip_hda_mode(struct pm8001_hba_info *pm8001_ha) +{ + int i = 0; + u32 arga[6]; + u32 reg; + u32 aap1_offset; + u32 fw_offset; + u32 pad1; + u32 pad2; + u8 *istr_buffer = NULL; + u32 istr_length = 0; + u8 *ila_buffer = NULL; + u32 ila_length = 0; + u32 aap1_length = 0; + u32 iop_length = 0; + u8 firmware_released = true; + + /*get initial string image*/ + if (request_firmware(&pm8001_ha->fw_image, "istrimg.bin", + pm8001_ha->dev) != 0) { + pm8001_printk("Can not get istrimg.bin\n"); + goto err_out_hda; + } + istr_length = pm8001_ha->fw_image->size; + pm8001_printk("Get istrimg.bin, length is %x\n", istr_length); + istr_buffer = kmalloc(pm8001_ha->fw_image->size, GFP_KERNEL); + if (istr_buffer == NULL) { + release_firmware(pm8001_ha->fw_image); + goto err_out_hda; + } + + memcpy(istr_buffer, pm8001_ha->fw_image->data, + pm8001_ha->fw_image->size); + release_firmware(pm8001_ha->fw_image); + + /*Get ILA image*/ + if (request_firmware(&pm8001_ha->fw_image, "ilaimg.bin", + pm8001_ha->dev) != 0) { + pm8001_printk("Can not get ilaimg.bin\n"); + goto err_out_hda; + } + + ila_length = pm8001_ha->fw_image->size; + pm8001_printk("Get ilaimg.bin, length is %x\n", ila_length); + ila_buffer = kmalloc(pm8001_ha->fw_image->size, GFP_KERNEL); + if (ila_buffer == NULL) { + release_firmware(pm8001_ha->fw_image); + goto err_out_hda; + } + + memcpy(ila_buffer, pm8001_ha->fw_image->data, + pm8001_ha->fw_image->size); + release_firmware(pm8001_ha->fw_image); + + /*get aap1 image*/ + if (request_firmware(&pm8001_ha->fw_image, "aap1img.bin", + pm8001_ha->dev) != 0) { + pm8001_printk("Can not get aap1img.bin\n"); + goto err_out_hda; + } + aap1_length = pm8001_ha->fw_image->size; + pm8001_printk("Get aap1img.bin, length is %x\n", aap1_length); + + firmware_released = false; + + /* Try soft reset until it goes into HDA mode */ + pm8001_chip_soft_rst(pm8001_ha, SPC_HDASOFT_RESET_SIGNATURE); + mdelay(10); + if (!pm8001_ishdar_idle(pm8001_ha)) { + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("SPC_HDASOFT_RESET: failed!\n")); + goto err_out_hda; + } + + /* HDA Mode - Clear ODMR and ODCR */ + pm8001_cw32(pm8001_ha, 0, MSGU_ODCR, ODCR_CLEAR_ALL); + pm8001_cw32(pm8001_ha, 0, MSGU_ODMR, ODMR_CLEAR_ALL); + + /* Step 1: Poll HDA_RSP_IDLE - HDA mode */ + i = 0; + do { + mdelay(10); + if (pm8001_ishdar_idle(pm8001_ha)) + break; + i++; + } while (i < 200); + if (i == 200) { + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("HDA Mode: Timeout!\n")); /* 2 sec */ + goto err_out_hda; + } + PM8001_INIT_DBG(pm8001_ha, pm8001_printk("HDA Mode!\n")); + + /* Step 2: Push the init string to 0x0047E000 & data compare */ + pm8001_printk("istrimage length is %x\n", istr_length); + if (!pm8001_bar4_cpy(pm8001_ha, GSM_HDA_ILA_STR_BASE, + GSM_ILA_STR_OFFSET, istr_buffer, istr_length)) + goto err_out_hda; + PM8001_INIT_DBG(pm8001_ha, pm8001_printk("ILA Str cpy done!\n")); + + /* Tell FW ISTR is ready */ + reg = (ILA_HDA_ISTR_IMG_DONE << 24) | istr_length; + pm8001_cw32(pm8001_ha, 0, MSGU_HOST_SCRATCH_PAD_3, reg); + + /* Step 3: Write the HDA mode SoftReset signature */ + pm8001_cw32(pm8001_ha, 0, MSGU_HOST_SCRATCH_PAD_0, + SPC_HDASOFT_RESET_SIGNATURE); + + /* Step 4: Push the ILA image to 0x00400000 */ + arga[1] = ila_length; + if (!pm8001_bar4_cpy(pm8001_ha, GSM_HDA_ILA_BASE, GSM_HDA_ILA_OFFSET, + ila_buffer, ila_length)) + goto err_out_hda; + PM8001_INIT_DBG(pm8001_ha, pm8001_printk("ILA cpy done!\n")); + + /* Step 5: Tell boot ROM to authenticate ILA and execute it */ + arga[0] = 0; + pm8001_hda_send_cmd(pm8001_ha, arga, 2, HDAC_CMD_EXEC); + PM8001_INIT_DBG(pm8001_ha, pm8001_printk("CMD EXEC sent!\n")); + + /* + * Step 6: Checking response status from boot ROM, + * HDAR_EXEC (good), HDAR_BAD_CMD and HDAR_BAD_IMG + */ + if (!pm8001_hda_recv_rsp(pm8001_ha, HDAC_CMD_EXEC)) + goto err_out_hda; + PM8001_INIT_DBG(pm8001_ha, pm8001_printk("CMD EXEC rsp ok!\n")); + + /* Step 7: Poll ILAHDA_AAP1IMGGET/Offset in MSGU Scratchpad 0 */ + /* Check MSGU Scratchpad 1 [1,0] == 00 */ + i = 0; + do { + mdelay(10); + reg = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_0); + aap1_offset = reg & ~SCRATCH_PAD0_STATE_MASK; + reg = reg >> 24; + if (reg == ILA_HDA_AAP1_IMG_GET) + break; + i++; + } while (i < 200); + if (i == 200) { + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("APP1_IMG_GET Poll timeout !\n")); + reg = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_1); + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("scratch pad 1 = 0x%x\n", reg)); + reg = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_2); + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("scratch pad 2 = 0x%x\n", reg)); + + goto err_out_hda; + } + PM8001_INIT_DBG(pm8001_ha, pm8001_printk("APP1 img get ok!\n")); + + /* Step 8: Copy AAP1 image, update the Host Scratchpad 3 */ + reg = (ILA_HDA_AAP1_IMG_DONE << 24) | aap1_length; + if (!pm8001_bar4_cpy_big(pm8001_ha, GSM_HDA_ILA_BASE, aap1_offset, + pm8001_ha->fw_image->data, aap1_length)) { + release_firmware(pm8001_ha->fw_image); + firmware_released = true; + goto err_out_hda; + } else { + release_firmware(pm8001_ha->fw_image); + firmware_released = true; + } + PM8001_INIT_DBG(pm8001_ha, pm8001_printk("APP1 cpy done!\n")); + + pm8001_cw32(pm8001_ha, 0, MSGU_HOST_SCRATCH_PAD_3, reg); + + + /*Get IOP image*/ + if (request_firmware(&pm8001_ha->fw_image, "iopimg.bin", + pm8001_ha->dev) != 0) { + pm8001_printk("Can not get istrimg.bin\n"); + goto err_out_hda; + } else { + iop_length = pm8001_ha->fw_image->size; + pm8001_printk("Get iopimg.bin, length is %x\n", iop_length); + firmware_released = false; + } + + /* Step 9: Poll ILAHDA_IOPIMGGET/Offset in MSGU Scratchpad 0 */ + i = 0; + do { + mdelay(10); + reg = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_0); + fw_offset = reg & ~SCRATCH_PAD0_STATE_MASK; + reg = reg >> 24; + if (reg == ILA_HDA_IOP_IMG_GET) + break; + i++; + } while (i < 200); + if (i == 200) { + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("IOP_IMG_GET Poll timeout !\n")); + + goto err_out_hda; + } + PM8001_INIT_DBG(pm8001_ha, pm8001_printk("IOP img get ok!\n")); + + /* Step 10: Copy IOP image, update the Host Scratchpad 3 */ + reg = (ILA_HDA_IOP_IMG_DONE << 24) | iop_length; + if (!pm8001_bar4_cpy_big(pm8001_ha, GSM_HDA_ILA_BASE, fw_offset, + pm8001_ha->fw_image->data, iop_length)) { + release_firmware(pm8001_ha->fw_image); + firmware_released = true; + goto err_out_hda; + } else { + release_firmware(pm8001_ha->fw_image); + firmware_released = true; + } + PM8001_INIT_DBG(pm8001_ha, pm8001_printk("IOP cpy done!\n")); + + pm8001_cw32(pm8001_ha, 0, MSGU_HOST_SCRATCH_PAD_3, reg); + + /* Clear the signature */ + pm8001_cw32(pm8001_ha, 0, MSGU_HOST_SCRATCH_PAD_0, 0); + + /* step 11: wait for the FW and IOP to get ready - 1 sec timeout */ + /* Wait for the SPC Configuration Table to be ready */ + i = 0; + do { + mdelay(10); + pad1 = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_1); + if (pad1 & SCRATCH_PAD1_RDY) { + pad2 = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_2); + if (pad2 & SCRATCH_PAD2_RDY) + break; + } + i++; + } while (i < 200); + + if (i == 200) { + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("PAD 1 & 2 not Rdy !\n")); + + goto err_out_hda; + } + PM8001_INIT_DBG(pm8001_ha, pm8001_printk("HDA Mode Complete!\n")); + + kfree(istr_buffer); + kfree(ila_buffer); + if (firmware_released == false) + release_firmware(pm8001_ha->fw_image); + + return 1; + +err_out_hda: + kfree(istr_buffer); + kfree(ila_buffer); + if (firmware_released == false) + release_firmware(pm8001_ha->fw_image); + + return 0; +} + /** * pm8001_chip_init - the main init function that initialize whole PM8001 chip. * @pm8001_ha: our hba card information @@ -704,40 +1187,60 @@ static u32 soft_reset_ready_check(struct pm8001_hba_info *pm8001_ha) PM8001_INIT_DBG(pm8001_ha, pm8001_printk("Firmware is ready for reset .\n")); } else { - unsigned long flags; - /* Trigger NMI twice via RB6 */ - spin_lock_irqsave(&pm8001_ha->lock, flags); - if (-1 == pm8001_bar4_shift(pm8001_ha, RB6_ACCESS_REG)) { - spin_unlock_irqrestore(&pm8001_ha->lock, flags); - PM8001_FAIL_DBG(pm8001_ha, - pm8001_printk("Shift Bar4 to 0x%x failed\n", - RB6_ACCESS_REG)); - return -1; - } - pm8001_cw32(pm8001_ha, 2, SPC_RB6_OFFSET, - RB6_MAGIC_NUMBER_RST); - pm8001_cw32(pm8001_ha, 2, SPC_RB6_OFFSET, RB6_MAGIC_NUMBER_RST); - /* wait for 100 ms */ - mdelay(100); - regVal = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_2) & - SCRATCH_PAD2_FWRDY_RST; - if (regVal != SCRATCH_PAD2_FWRDY_RST) { - regVal1 = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_1); - regVal2 = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_2); - PM8001_FAIL_DBG(pm8001_ha, - pm8001_printk("TIMEOUT:MSGU_SCRATCH_PAD1" - "=0x%x, MSGU_SCRATCH_PAD2=0x%x\n", - regVal1, regVal2)); - PM8001_FAIL_DBG(pm8001_ha, - pm8001_printk("SCRATCH_PAD0 value = 0x%x\n", - pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_0))); - PM8001_FAIL_DBG(pm8001_ha, - pm8001_printk("SCRATCH_PAD3 value = 0x%x\n", - pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_3))); + if (pm8001_ishdar_idle(pm8001_ha)) { + /* + * For customers wants to do soft reset even the + * chip is already in HDA mode + * + * Do not need to trigger RB6 twice + */ + ; + } else { + unsigned long flags; + /* Trigger NMI twice via RB6 */ + spin_lock_irqsave(&pm8001_ha->lock, flags); + if (-1 == pm8001_bar4_shift(pm8001_ha, + RB6_ACCESS_REG)) { + spin_unlock_irqrestore(&pm8001_ha->lock, flags); + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk("Shift Bar4 to 0x%x " + "failed\n", + RB6_ACCESS_REG)); + return -1; + } + pm8001_cw32(pm8001_ha, 2, SPC_RB6_OFFSET, + RB6_MAGIC_NUMBER_RST); + pm8001_cw32(pm8001_ha, 2, SPC_RB6_OFFSET, + RB6_MAGIC_NUMBER_RST); + /* wait for 100 ms */ + mdelay(100); + regVal = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_2) & + SCRATCH_PAD2_FWRDY_RST; + if (regVal != SCRATCH_PAD2_FWRDY_RST) { + regVal1 = pm8001_cr32(pm8001_ha, 0, + MSGU_SCRATCH_PAD_1); + regVal2 = pm8001_cr32(pm8001_ha, 0, + MSGU_SCRATCH_PAD_2); + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk("TIMEOUT:" + "MSGU_SCRATCH_PAD1=0x%x, " + "MSGU_SCRATCH_PAD2=0x%x\n", + regVal1, regVal2)); + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk("SCRATCH_PAD0 " + "value = 0x%x\n", + pm8001_cr32(pm8001_ha, 0, + MSGU_SCRATCH_PAD_0))); + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk("SCRATCH_PAD3 " + "value = 0x%x\n", + pm8001_cr32(pm8001_ha, 0, + MSGU_SCRATCH_PAD_3))); + spin_unlock_irqrestore(&pm8001_ha->lock, flags); + return -1; + } spin_unlock_irqrestore(&pm8001_ha->lock, flags); - return -1; } - spin_unlock_irqrestore(&pm8001_ha->lock, flags); } return 0; } @@ -1009,7 +1512,10 @@ pm8001_chip_soft_rst(struct pm8001_hba_info *pm8001_ha, u32 signature) pm8001_cw32(pm8001_ha, 2, SPC_REG_RESET, regVal); /* step 14: delay 10 usec - Normal Mode */ - udelay(10); + if (signature == SPC_SOFT_RESET_SIGNATURE) + udelay(10); + else + mdelay(200); /* check Soft Reset Normal mode or Soft Reset HDA mode */ if (signature == SPC_SOFT_RESET_SIGNATURE) { /* step 15 (Normal Mode): wait until scratch pad1 register @@ -4683,6 +5189,8 @@ pm8001_chip_sas_re_initialization(struct pm8001_hba_info *pm8001_ha) const struct pm8001_dispatch pm8001_8001_dispatch = { .name = "pmc8001", + .chip_in_hda_mode = pm8001_chip_in_hda_mode, + .chip_hda_mode = pm8001_chip_hda_mode, .chip_init = pm8001_chip_init, .chip_soft_rst = pm8001_chip_soft_rst, .chip_rst = pm8001_hw_chip_rst, diff --git a/drivers/scsi/pm8001/pm8001_hwi.h b/drivers/scsi/pm8001/pm8001_hwi.h index 1a4611e..2f178e0 100644 --- a/drivers/scsi/pm8001/pm8001_hwi.h +++ b/drivers/scsi/pm8001/pm8001_hwi.h @@ -855,6 +855,16 @@ struct set_dev_state_resp { #define MSIX_INTERRUPT_DISABLE 0x1 #define MSIX_INTERRUPT_ENABLE 0x0 +/* ILA codes in MSGU Scratchpad 0 */ +#define ILA_HDA_IOP_IMG_GET 0x10 +#define ILA_HDA_AAP1_IMG_GET 0x11 +#define ILA_HDA_AAP2_IMG_GET 0x12 +#define ILA_HDA_EXITGOOD 0x1F +#define ILA_HDA_IOP_IMG_DONE 0x80 +#define ILA_HDA_AAP1_IMG_DONE 0x81 +#define ILA_HDA_ISTR_IMG_DONE 0x83 + +#define SCRATCH_PAD0_STATE_MASK 0xFF000000 /* state definition for Scratch Pad1 register */ #define SCRATCH_PAD1_POR 0x00 /* power on reset state */ @@ -952,8 +962,10 @@ struct set_dev_state_resp { #define PCIE_ERROR_INTERRUPT_ENABLE 0x003048 #define PCIE_ERROR_INTERRUPT 0x00304C /* signature definition for host scratch pad0 register */ -#define SPC_SOFT_RESET_SIGNATURE 0x252acbcd /* Signature for Soft Reset */ +#define SPC_SOFT_RESET_SIGNATURE 0x252acbcd +/* Signature for HDA Soft Reset without PCIe resetting */ +#define SPC_HDASOFT_RESET_SIGNATURE 0xa5aa27d7 /* SPC Reset register - BAR4(0x20), BAR2(win) (need dynamic mapping) */ #define SPC_REG_RESET 0x000000/* reset register */ @@ -987,6 +999,14 @@ struct set_dev_state_resp { #define MBIC_AAP1_ADDR_BASE 0x060000 #define MBIC_IOP_ADDR_BASE 0x070000 #define GSM_ADDR_BASE 0x0700000 +#define GSM_HDA_ILA_STR_BASE 0x470000 +#define GSM_ILA_STR_OFFSET 0xE000 +#define GSM_HDA_ILA_BASE 0x400000 +#define GSM_HDA_ILA_OFFSET 0x0 +#define SIZE_64KB 0x00010000 +#define MB3_SHIFT_MASK 0xFFFF0000 +#define MB3_OFFSET_MASK 0x0000FFFF + /* Dynamic map through Bar4 - 0x00700000 */ #define GSM_CONFIG_RESET 0x00000000 #define RAM_ECC_DB_ERR 0x00000018 @@ -998,10 +1018,23 @@ struct set_dev_state_resp { #define GSM_WRITE_DATA_PARITY_CHECK 0x00000048 #define RB6_ACCESS_REG 0x6A0000 -#define HDAC_EXEC_CMD 0x0002 +#define HDA_CMD_OFFSET 0xfec0 +#define HDA_RSP_OFFSET 0xfee0 +#define HDAC_CMD_BUF_INFO 0x0001 +#define HDAC_CMD_EXEC 0x0002 +#define HDAC_CMD_RESET 0x0003 #define HDA_C_PA 0xcb +#define HDA_R_PA 0xdb +#define HDA_PA_BITS 0xff000000 #define HDA_SEQ_ID_BITS 0x00ff0000 +#define HDA_CODE_BITS 0x0000ffff #define HDA_GSM_OFFSET_BITS 0x00FFFFFF +#define HDA_RSP_BUF_INFO 0x8001 +#define HDA_RSP_IDLE 0x8002 +#define HDA_RSP_BAD_IMG 0x8003 +#define HDA_RSP_BAD_CMD 0x8004 +#define HDA_RSP_INTL_ERR 0x8005 +#define HDA_RSP_EXEC 0x8006 #define MBIC_AAP1_ADDR_BASE 0x060000 #define MBIC_IOP_ADDR_BASE 0x070000 #define GSM_ADDR_BASE 0x0700000 diff --git a/drivers/scsi/pm8001/pm8001_init.c b/drivers/scsi/pm8001/pm8001_init.c index 36efaa7..db5bd77 100644 --- a/drivers/scsi/pm8001/pm8001_init.c +++ b/drivers/scsi/pm8001/pm8001_init.c @@ -41,6 +41,7 @@ #include #include "pm8001_sas.h" #include "pm8001_chips.h" +#include "pm8001_hwi.h" static struct scsi_transport_template *pm8001_stt; @@ -669,8 +670,35 @@ static int __devinit pm8001_pci_probe(struct pci_dev *pdev, rc = -ENOMEM; goto err_out_free; } + /* + * Make sure we have at least a sane region 0 + * + * We do this here because a failure in pm8001_pci_alloc + * is just interpreted as a memory allocation failure. + */ + if (pm8001_ha->io_mem[0].memvirtaddr == NULL + || pm8001_ha->io_mem[0].memsize < (1 << 15)) { + printk(KERN_ERR "BAR0 access bad: %p,%x\n", + pm8001_ha->io_mem[0].memvirtaddr, + pm8001_ha->io_mem[0].memsize); + rc = -ENXIO; + goto err_out_free; + } list_add_tail(&pm8001_ha->list, &hba_list); - PM8001_CHIP_DISP->chip_soft_rst(pm8001_ha, 0x252acbcd); + + /* HDA SEEPROM Force HDA Mode */ + if (PM8001_CHIP_DISP->chip_in_hda_mode(pm8001_ha)) { + rc = PM8001_CHIP_DISP->chip_hda_mode(pm8001_ha); + if (!rc) { + rc = -EBUSY; + goto err_out_ha_free; + } + pm8001_ha->rst_signature = SPC_HDASOFT_RESET_SIGNATURE; + } else { + PM8001_CHIP_DISP->chip_soft_rst(pm8001_ha, + SPC_SOFT_RESET_SIGNATURE); + pm8001_ha->rst_signature = SPC_SOFT_RESET_SIGNATURE; + } rc = PM8001_CHIP_DISP->chip_init(pm8001_ha); if (rc) goto err_out_ha_free; diff --git a/drivers/scsi/pm8001/pm8001_sas.h b/drivers/scsi/pm8001/pm8001_sas.h index 1100820..4b7f2e0 100644 --- a/drivers/scsi/pm8001/pm8001_sas.h +++ b/drivers/scsi/pm8001/pm8001_sas.h @@ -130,6 +130,8 @@ struct pm8001_ioctl_payload { struct pm8001_dispatch { char *name; + int (*chip_in_hda_mode)(struct pm8001_hba_info *pm8001_ha); + int (*chip_hda_mode)(struct pm8001_hba_info *pm8001_ha); int (*chip_init)(struct pm8001_hba_info *pm8001_ha); int (*chip_soft_rst)(struct pm8001_hba_info *pm8001_ha, u32 signature); void (*chip_rst)(struct pm8001_hba_info *pm8001_ha); @@ -384,6 +386,7 @@ struct pm8001_hba_info { u32 logging_level; u32 fw_status; const struct firmware *fw_image; + u32 rst_signature; }; struct pm8001_work { --Apple-Mail=_12FA110C-D8A2-412B-9446-7E8B9E6F348A--