/* * PMC-Sierra SPCv/ve 8088/8089 SAS/SATA based host adapters driver * * Copyright (c) 2008-2009 PMC-Sierra, Inc., * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions, and the following disclaimer, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * substantially similar to the "NO WARRANTY" disclaimer below * ("Disclaimer") and any redistribution must be conditioned upon * including a substantially similar Disclaimer requirement for further * binary redistribution. * 3. Neither the names of the above-listed copyright holders nor the names * of any contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * Alternatively, this software may be distributed under the terms of the * GNU General Public License ("GPL") version 2 as published by the Free * Software Foundation. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGES. *
*/ #include <linux/slab.h> #include"pm8001_sas.h" #include"pm80xx_hwi.h" #include"pm8001_chips.h" #include"pm8001_ctl.h" #include"pm80xx_tracepoints.h"
#define SMP_DIRECT 1 #define SMP_INDIRECT 2
int pm80xx_bar4_shift(struct pm8001_hba_info *pm8001_ha, u32 shift_value)
{
u32 reg_val; unsignedlong start;
pm8001_cw32(pm8001_ha, 0, MEMBASE_II_SHIFT_REGISTER, shift_value); /* confirm the setting is written */
start = jiffies + HZ; /* 1 sec */ do {
reg_val = pm8001_cr32(pm8001_ha, 0, MEMBASE_II_SHIFT_REGISTER);
} while ((reg_val != shift_value) && time_before(jiffies, start)); if (reg_val != shift_value) {
pm8001_dbg(pm8001_ha, FAIL, "TIMEOUT:MEMBASE_II_SHIFT_REGISTER = 0x%x\n",
reg_val); return -1;
} return 0;
}
pm8001_ha->forensic_info.data_buf.direct_data = direct_data;
pm8001_dbg(pm8001_ha, IO, "ossaHwCB: status1 %d\n", status);
pm8001_dbg(pm8001_ha, IO, "ossaHwCB: read_len 0x%x\n",
pm8001_ha->forensic_info.data_buf.read_len);
pm8001_dbg(pm8001_ha, IO, "ossaHwCB: direct_len 0x%x\n",
pm8001_ha->forensic_info.data_buf.direct_len);
pm8001_dbg(pm8001_ha, IO, "ossaHwCB: direct_offset 0x%x\n",
pm8001_ha->forensic_info.data_buf.direct_offset);
} if (pm8001_ha->forensic_info.data_buf.direct_offset == 0) { /* start to get data */ /* Program the MEMBASE II Shifting Register with 0x00.*/
pm8001_cw32(pm8001_ha, 0, MEMBASE_II_SHIFT_REGISTER,
pm8001_ha->fatal_forensic_shift_offset);
pm8001_ha->forensic_last_offset = 0;
pm8001_ha->forensic_fatal_step = 0;
pm8001_ha->fatal_bar_loc = 0;
}
/* Read until accum_len is retrieved */
accum_len = pm8001_mr32(fatal_table_address,
MPI_FATAL_EDUMP_TABLE_ACCUM_LEN); /* Determine length of data between previously stored transfer length * and current accumulated transfer length
*/
length_to_read =
accum_len - pm8001_ha->forensic_preserved_accumulated_transfer;
pm8001_dbg(pm8001_ha, IO, "get_fatal_spcv: accum_len 0x%x\n",
accum_len);
pm8001_dbg(pm8001_ha, IO, "get_fatal_spcv: length_to_read 0x%x\n",
length_to_read);
pm8001_dbg(pm8001_ha, IO, "get_fatal_spcv: last_offset 0x%x\n",
pm8001_ha->forensic_last_offset);
pm8001_dbg(pm8001_ha, IO, "get_fatal_spcv: read_len 0x%x\n",
pm8001_ha->forensic_info.data_buf.read_len);
pm8001_dbg(pm8001_ha, IO, "get_fatal_spcv:: direct_len 0x%x\n",
pm8001_ha->forensic_info.data_buf.direct_len);
pm8001_dbg(pm8001_ha, IO, "get_fatal_spcv:: direct_offset 0x%x\n",
pm8001_ha->forensic_info.data_buf.direct_offset);
/* If accumulated length failed to read correctly fail the attempt.*/ if (accum_len == 0xFFFFFFFF) {
pm8001_dbg(pm8001_ha, IO, "Possible PCI issue 0x%x not expected\n",
accum_len); return status;
} /* If accumulated length is zero fail the attempt */ if (accum_len == 0) {
pm8001_ha->forensic_info.data_buf.direct_data +=
sprintf(pm8001_ha->forensic_info.data_buf.direct_data, "%08x ", 0xFFFFFFFF); return (char *)pm8001_ha->forensic_info.data_buf.direct_data -
(char *)buf;
} /* Accumulated length is good so start capturing the first data */
temp = (u32 *)pm8001_ha->memoryMap.region[FORENSIC_MEM].virt_ptr; if (pm8001_ha->forensic_fatal_step == 0) {
moreData: /* If data to read is less than SYSFS_OFFSET then reduce the * length of dataLen
*/ if (pm8001_ha->forensic_last_offset + SYSFS_OFFSET
> length_to_read) {
pm8001_ha->forensic_info.data_buf.direct_len =
length_to_read -
pm8001_ha->forensic_last_offset;
} else {
pm8001_ha->forensic_info.data_buf.direct_len =
SYSFS_OFFSET;
} if (pm8001_ha->forensic_info.data_buf.direct_data) { /* Data is in bar, copy to host memory */
pm80xx_pci_mem_copy(pm8001_ha,
pm8001_ha->fatal_bar_loc,
pm8001_ha->memoryMap.region[FORENSIC_MEM].virt_ptr,
pm8001_ha->forensic_info.data_buf.direct_len, 1);
}
pm8001_ha->fatal_bar_loc +=
pm8001_ha->forensic_info.data_buf.direct_len;
pm8001_ha->forensic_info.data_buf.direct_offset +=
pm8001_ha->forensic_info.data_buf.direct_len;
pm8001_ha->forensic_last_offset +=
pm8001_ha->forensic_info.data_buf.direct_len;
pm8001_ha->forensic_info.data_buf.read_len =
pm8001_ha->forensic_info.data_buf.direct_len;
/* Increment the MEMBASE II Shifting Register value by 0x100.*/
pm8001_ha->forensic_info.data_buf.direct_data +=
sprintf(pm8001_ha->forensic_info.data_buf.direct_data, "%08x ", 2); for (index = 0; index <
(pm8001_ha->forensic_info.data_buf.direct_len
/ 4) ; index++) {
pm8001_ha->forensic_info.data_buf.direct_data +=
sprintf(pm8001_ha->
forensic_info.data_buf.direct_data, "%08x ", *(temp + index));
}
pm8001_ha->fatal_forensic_shift_offset += 0x100;
pm8001_cw32(pm8001_ha, 0, MEMBASE_II_SHIFT_REGISTER,
pm8001_ha->fatal_forensic_shift_offset);
pm8001_ha->fatal_bar_loc = 0;
status = 0;
offset = (int)
((char *)pm8001_ha->forensic_info.data_buf.direct_data
- (char *)buf);
pm8001_dbg(pm8001_ha, IO, "get_fatal_spcv: return3 0x%x\n",
offset); return (char *)pm8001_ha->forensic_info.data_buf.direct_data -
(char *)buf;
} if (pm8001_ha->forensic_fatal_step == 1) { /* store previous accumulated length before triggering next * accumulated length update
*/
pm8001_ha->forensic_preserved_accumulated_transfer =
pm8001_mr32(fatal_table_address,
MPI_FATAL_EDUMP_TABLE_ACCUM_LEN);
/* continue capturing the fatal log until Dump status is 0x3 */ if (pm8001_mr32(fatal_table_address,
MPI_FATAL_EDUMP_TABLE_STATUS) <
MPI_FATAL_EDUMP_TABLE_STAT_NF_SUCCESS_DONE) {
/* reset fddstat bit by writing to zero*/
pm8001_mw32(fatal_table_address,
MPI_FATAL_EDUMP_TABLE_STATUS, 0x0);
/* set dump control value to '1' so that new data will * be transferred to shared memory
*/
pm8001_mw32(fatal_table_address,
MPI_FATAL_EDUMP_TABLE_HANDSHAKE,
MPI_FATAL_EDUMP_HANDSHAKE_RDY);
temp = (u32 *)pm8001_ha->memoryMap.region[FORENSIC_MEM].virt_ptr; if (++pm8001_ha->non_fatal_count == 1) { if (pm8001_ha->chip_id == chip_8001) {
snprintf(pm8001_ha->forensic_info.data_buf.direct_data,
PAGE_SIZE, "Not supported for SPC controller"); return 0;
}
pm8001_dbg(pm8001_ha, IO, "forensic_info TYPE_NON_FATAL...\n"); /* * Step 1: Write the host buffer parameters in the MPI Fatal and * Non-Fatal Error Dump Capture Table.This is the buffer * where debug data will be DMAed to.
*/
pm8001_mw32(nonfatal_table_address,
MPI_FATAL_EDUMP_TABLE_LO_OFFSET,
pm8001_ha->memoryMap.region[FORENSIC_MEM].phys_addr_lo);
/* Optionally, set the DUMPCTRL bit to 1 if the host * keeps sending active I/Os while capturing the non-fatal * debug data. Otherwise, leave this bit set to zero
*/
pm8001_mw32(nonfatal_table_address,
MPI_FATAL_EDUMP_TABLE_HANDSHAKE, MPI_FATAL_EDUMP_HANDSHAKE_RDY);
/* * Step 2: Clear Accumulative Length of Debug Data Transferred * [ACCDDLEN] field in the MPI Fatal and Non-Fatal Error Dump * Capture Table to zero.
*/
pm8001_mw32(nonfatal_table_address,
MPI_FATAL_EDUMP_TABLE_ACCUM_LEN, 0);
total_len = pm8001_mr32(nonfatal_table_address,
MPI_FATAL_EDUMP_TABLE_TOTAL_LEN); /* * Step 3:Clear Fatal/Non-Fatal Debug Data Transfer Status [FDDTSTAT] * field and then request that the SPCv controller transfer the debug * data by setting bit 7 of the Inbound Doorbell Set Register.
*/
pm8001_mw32(nonfatal_table_address, MPI_FATAL_EDUMP_TABLE_STATUS, 0);
pm8001_cw32(pm8001_ha, 0, MSGU_IBDB_SET,
SPCv_MSGU_CFG_TABLE_NONFATAL_DUMP);
/* * Step 4.1: Read back the Inbound Doorbell Set Register (by polling for * 2 seconds) until register bit 7 is cleared. * This step only indicates the request is accepted by the controller.
*/
start = jiffies + (2 * HZ); /* 2 sec */ do {
reg_val = pm8001_cr32(pm8001_ha, 0, MSGU_IBDB_SET) &
SPCv_MSGU_CFG_TABLE_NONFATAL_DUMP;
} while ((reg_val != 0) && time_before(jiffies, start));
/* Step 4.2: To check the completion of the transfer, poll the Fatal/Non * Fatal Debug Data Transfer Status [FDDTSTAT] field for 2 seconds in * the MPI Fatal and Non-Fatal Error Dump Capture Table.
*/
start = jiffies + (2 * HZ); /* 2 sec */ do {
reg_val = pm8001_mr32(nonfatal_table_address,
MPI_FATAL_EDUMP_TABLE_STATUS);
} while ((!reg_val) && time_before(jiffies, start));
/* store current accumulated length to use in next iteration as * the previous accumulated length
*/
pm8001_ha->forensic_preserved_accumulated_transfer = accum_len; return (buf_copy - buf);
}
/** * read_main_config_table - read the configure table and save it. * @pm8001_ha: our hba card information
*/ staticvoid read_main_config_table(struct pm8001_hba_info *pm8001_ha)
{ void __iomem *address = pm8001_ha->main_cfg_tbl_addr;
/* read GPIO LED settings from the configuration table */
pm8001_ha->main_cfg_tbl.pm80xx_tbl.gpio_led_mapping =
pm8001_mr32(address, MAIN_GPIO_LED_FLAGS_OFFSET);
/* read analog Setting offset from the configuration table */
pm8001_ha->main_cfg_tbl.pm80xx_tbl.analog_setup_table_offset =
pm8001_mr32(address, MAIN_ANALOG_SETUP_OFFSET);
pm8001_ha->main_cfg_tbl.pm80xx_tbl.int_vec_table_offset =
pm8001_mr32(address, MAIN_INT_VECTOR_TABLE_OFFSET);
pm8001_ha->main_cfg_tbl.pm80xx_tbl.phy_attr_table_offset =
pm8001_mr32(address, MAIN_SAS_PHY_ATTR_TABLE_OFFSET); /* read port recover and reset timeout */
pm8001_ha->main_cfg_tbl.pm80xx_tbl.port_recovery_timer =
pm8001_mr32(address, MAIN_PORT_RECOVERY_TIMER); /* read ILA and inactive firmware version */
pm8001_ha->main_cfg_tbl.pm80xx_tbl.ila_version =
pm8001_mr32(address, MAIN_MPI_ILA_RELEASE_TYPE);
pm8001_ha->main_cfg_tbl.pm80xx_tbl.inc_fw_version =
pm8001_mr32(address, MAIN_MPI_INACTIVE_FW_VERSION);
/* Enable higher IQs and OQs, 32 to 63, bit 16 */ if (pm8001_ha->max_q_num > 32)
pm8001_ha->main_cfg_tbl.pm80xx_tbl.fatal_err_interrupt |=
1 << 16; /* Disable end to end CRC checking */
pm8001_ha->main_cfg_tbl.pm80xx_tbl.crc_core_dump = (0x1 << 16);
/* SPCv specific */
pm8001_ha->main_cfg_tbl.pm80xx_tbl.gpio_led_mapping &= 0xCFFFFFFF; /* Set GPIOLED to 0x2 for LED indicator */
pm8001_ha->main_cfg_tbl.pm80xx_tbl.gpio_led_mapping |= 0x20000000;
pm8001_mw32(address, MAIN_GPIO_LED_FLAGS_OFFSET,
pm8001_ha->main_cfg_tbl.pm80xx_tbl.gpio_led_mapping);
pm8001_dbg(pm8001_ha, DEV, "Programming DW 0x21 in main cfg table with 0x%x\n",
pm8001_mr32(address, MAIN_GPIO_LED_FLAGS_OFFSET));
/** * update_inbnd_queue_table - update the inbound queue table to the HBA. * @pm8001_ha: our hba card information * @number: entry in the queue
*/ staticvoid update_inbnd_queue_table(struct pm8001_hba_info *pm8001_ha, int number)
{ void __iomem *address = pm8001_ha->inbnd_q_tbl_addr;
u16 offset = number * 0x20;
pm8001_mw32(address, offset + IB_PROPERITY_OFFSET,
pm8001_ha->inbnd_q_tbl[number].element_pri_size_cnt);
pm8001_mw32(address, offset + IB_BASE_ADDR_HI_OFFSET,
pm8001_ha->inbnd_q_tbl[number].upper_base_addr);
pm8001_mw32(address, offset + IB_BASE_ADDR_LO_OFFSET,
pm8001_ha->inbnd_q_tbl[number].lower_base_addr);
pm8001_mw32(address, offset + IB_CI_BASE_ADDR_HI_OFFSET,
pm8001_ha->inbnd_q_tbl[number].ci_upper_base_addr);
pm8001_mw32(address, offset + IB_CI_BASE_ADDR_LO_OFFSET,
pm8001_ha->inbnd_q_tbl[number].ci_lower_base_addr);
pm8001_dbg(pm8001_ha, DEV, "IQ %d: Element pri size 0x%x\n",
number,
pm8001_ha->inbnd_q_tbl[number].element_pri_size_cnt);
pm8001_dbg(pm8001_ha, DEV, "IQ upr base addr 0x%x IQ lwr base addr 0x%x\n",
pm8001_ha->inbnd_q_tbl[number].upper_base_addr,
pm8001_ha->inbnd_q_tbl[number].lower_base_addr);
pm8001_dbg(pm8001_ha, DEV, "CI upper base addr 0x%x CI lower base addr 0x%x\n",
pm8001_ha->inbnd_q_tbl[number].ci_upper_base_addr,
pm8001_ha->inbnd_q_tbl[number].ci_lower_base_addr);
}
/** * update_outbnd_queue_table - update the outbound queue table to the HBA. * @pm8001_ha: our hba card information * @number: entry in the queue
*/ staticvoid update_outbnd_queue_table(struct pm8001_hba_info *pm8001_ha, int number)
{ void __iomem *address = pm8001_ha->outbnd_q_tbl_addr;
u16 offset = number * 0x24;
pm8001_mw32(address, offset + OB_PROPERITY_OFFSET,
pm8001_ha->outbnd_q_tbl[number].element_size_cnt);
pm8001_mw32(address, offset + OB_BASE_ADDR_HI_OFFSET,
pm8001_ha->outbnd_q_tbl[number].upper_base_addr);
pm8001_mw32(address, offset + OB_BASE_ADDR_LO_OFFSET,
pm8001_ha->outbnd_q_tbl[number].lower_base_addr);
pm8001_mw32(address, offset + OB_PI_BASE_ADDR_HI_OFFSET,
pm8001_ha->outbnd_q_tbl[number].pi_upper_base_addr);
pm8001_mw32(address, offset + OB_PI_BASE_ADDR_LO_OFFSET,
pm8001_ha->outbnd_q_tbl[number].pi_lower_base_addr);
pm8001_mw32(address, offset + OB_INTERRUPT_COALES_OFFSET,
pm8001_ha->outbnd_q_tbl[number].interrup_vec_cnt_delay);
pm8001_dbg(pm8001_ha, DEV, "OQ %d: Element pri size 0x%x\n",
number,
pm8001_ha->outbnd_q_tbl[number].element_size_cnt);
pm8001_dbg(pm8001_ha, DEV, "OQ upr base addr 0x%x OQ lwr base addr 0x%x\n",
pm8001_ha->outbnd_q_tbl[number].upper_base_addr,
pm8001_ha->outbnd_q_tbl[number].lower_base_addr);
pm8001_dbg(pm8001_ha, DEV, "PI upper base addr 0x%x PI lower base addr 0x%x\n",
pm8001_ha->outbnd_q_tbl[number].pi_upper_base_addr,
pm8001_ha->outbnd_q_tbl[number].pi_lower_base_addr);
}
/* * As per controller datasheet, after successful MPI * initialization minimum 500ms delay is required before * issuing commands.
*/
msleep(500);
return 0;
}
/** * check_fw_ready - The LLDD check if the FW is ready, if not, return error. * This function sleeps hence it must not be used in atomic context. * @pm8001_ha: our hba card information
*/ staticint check_fw_ready(struct pm8001_hba_info *pm8001_ha)
{
u32 value;
u32 max_wait_count;
u32 max_wait_time;
u32 expected_mask; int ret = 0;
/* reset / PCIe ready */
max_wait_time = max_wait_count = 5; /* 100 milli sec */ do {
msleep(FW_READY_INTERVAL);
value = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_1);
} while ((value == 0xFFFFFFFF) && (--max_wait_count));
/* check ila, RAAE and iops status */ if ((pm8001_ha->chip_id != chip_8008) &&
(pm8001_ha->chip_id != chip_8009)) {
max_wait_time = max_wait_count = 180; /* 3600 milli sec */
expected_mask = SCRATCH_PAD_ILA_READY |
SCRATCH_PAD_RAAE_READY |
SCRATCH_PAD_IOP0_READY |
SCRATCH_PAD_IOP1_READY;
} else {
max_wait_time = max_wait_count = 170; /* 3400 milli sec */
expected_mask = SCRATCH_PAD_ILA_READY |
SCRATCH_PAD_RAAE_READY |
SCRATCH_PAD_IOP0_READY;
} do {
msleep(FW_READY_INTERVAL);
value = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_1);
} while (((value & expected_mask) !=
expected_mask) && (--max_wait_count)); if (!max_wait_count) {
pm8001_dbg(pm8001_ha, INIT, "At least one FW component failed to load within %d millisec: Scratchpad1: 0x%x\n",
max_wait_time * FW_READY_INTERVAL, value);
ret = -1;
} else {
pm8001_dbg(pm8001_ha, MSG, "All FW components ready by %d ms\n",
(max_wait_time - max_wait_count) * FW_READY_INTERVAL);
} return ret;
}
value = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_0);
/* * lower 26 bits of SCRATCHPAD0 register describes offset within the * PCIe BAR where the MPI configuration table is present
*/
offset = value & 0x03FFFFFF; /* scratch pad 0 TBL address */
pm8001_dbg(pm8001_ha, DEV, "Scratchpad 0 Offset: 0x%x value 0x%x\n",
offset, value); /* * Upper 6 bits describe the offset within PCI config space where BAR * is located.
*/
pcilogic = (value & 0xFC000000) >> 26;
pcibar = get_pci_bar_index(pcilogic);
pm8001_dbg(pm8001_ha, INIT, "Scratchpad 0 PCI BAR: %d\n", pcibar);
/* * Make sure the offset falls inside the ioremapped PCI BAR
*/ if (offset > pm8001_ha->io_mem[pcibar].memsize) {
pm8001_dbg(pm8001_ha, FAIL, "Main cfg tbl offset outside %u > %u\n",
offset, pm8001_ha->io_mem[pcibar].memsize); return -EBUSY;
}
pm8001_ha->main_cfg_tbl_addr = base_addr =
pm8001_ha->io_mem[pcibar].memvirtaddr + offset;
payload.tag = cpu_to_le32(tag); /* Currently only one key is used. New KEK index is 1. * Current KEK index is 1. Store KEK to NVRAM is 1.
*/
payload.new_curidx_ksop =
cpu_to_le32(((1 << 24) | (1 << 16) | (1 << 8) |
KEK_MGMT_SUBOP_KEYCARDUPDATE));
pm8001_dbg(pm8001_ha, DEV, "Saving Encryption info to flash. payload 0x%x\n",
le32_to_cpu(payload.new_curidx_ksop));
/** * pm80xx_chip_init - the main init function that initializes whole PM8001 chip. * @pm8001_ha: our hba card information
*/ staticint pm80xx_chip_init(struct pm8001_hba_info *pm8001_ha)
{ int ret;
u8 i = 0;
/* check the firmware status */ if (-1 == check_fw_ready(pm8001_ha)) {
pm8001_dbg(pm8001_ha, FAIL, "Firmware is not ready!\n"); return -EBUSY;
}
/* Initialize the controller fatal error flag */
pm8001_ha->controller_fatal_error = false;
/* Initialize pci space address eg: mpi offset */
ret = init_pci_device_addresses(pm8001_ha); if (ret) {
pm8001_dbg(pm8001_ha, FAIL, "Failed to init pci addresses"); return ret;
}
init_default_table_values(pm8001_ha);
read_main_config_table(pm8001_ha);
read_general_status_table(pm8001_ha);
read_inbnd_queue_table(pm8001_ha);
read_outbnd_queue_table(pm8001_ha);
read_phy_attr_table(pm8001_ha);
/* update main config table ,inbound table and outbound table */
update_main_config_table(pm8001_ha); for (i = 0; i < pm8001_ha->max_q_num; i++) {
update_inbnd_queue_table(pm8001_ha, i);
update_outbnd_queue_table(pm8001_ha, i);
} /* notify firmware update finished and check initialization status */ if (0 == mpi_init_check(pm8001_ha)) {
pm8001_dbg(pm8001_ha, INIT, "MPI initialize successful!\n");
} else return -EBUSY;
return 0;
}
staticvoid pm80xx_chip_post_init(struct pm8001_hba_info *pm8001_ha)
{ /* send SAS protocol timer configuration page to FW */
pm80xx_set_sas_protocol_timer_config(pm8001_ha);
/* Check for encryption */ if (pm8001_ha->chip->encrypt) { int ret;
pm8001_dbg(pm8001_ha, INIT, "Checking for encryption\n");
ret = pm80xx_get_encrypt_info(pm8001_ha); if (ret == -1) {
pm8001_dbg(pm8001_ha, INIT, "Encryption error !!\n"); if (pm8001_ha->encrypt_info.status == 0x81) {
pm8001_dbg(pm8001_ha, INIT, "Encryption enabled with error.Saving encryption key to flash\n");
pm80xx_encrypt_update(pm8001_ha);
}
}
}
}
ret = init_pci_device_addresses(pm8001_ha); if (ret) {
pm8001_dbg(pm8001_ha, FAIL, "Failed to init pci addresses"); return ret;
}
/* Write bit1=1 to Inbound DoorBell Register to tell the SPC FW the
table is stop */
pm8001_cw32(pm8001_ha, 0, MSGU_IBDB_SET, SPCv_MSGU_CFG_TABLE_RESET);
/* wait until Inbound DoorBell Clear Register toggled */ if (IS_SPCV_12G(pm8001_ha->pdev)) {
max_wait_count = SPCV_DOORBELL_CLEAR_TIMEOUT;
} else {
max_wait_count = SPC_DOORBELL_CLEAR_TIMEOUT;
} do {
msleep(FW_READY_INTERVAL);
value = pm8001_cr32(pm8001_ha, 0, MSGU_IBDB_SET);
value &= SPCv_MSGU_CFG_TABLE_RESET;
} while ((value != 0) && (--max_wait_count));
env = kzalloc(sizeof(struct kobj_uevent_env), GFP_KERNEL); if (!env) return;
if (add_uevent_var(env, "DRIVER=%s", DRV_NAME)) gotoexit;
if (add_uevent_var(env, "HBA_NUM=%u", pm8001_ha->id)) gotoexit;
if (add_uevent_var(env, "EVENT_TYPE=FATAL_ERROR")) gotoexit;
switch (error_reporter) { case REPORTER_DRIVER: if (add_uevent_var(env, "REPORTED_BY=DRIVER")) gotoexit; break; case REPORTER_FIRMWARE: if (add_uevent_var(env, "REPORTED_BY=FIRMWARE")) gotoexit; break; default: if (add_uevent_var(env, "REPORTED_BY=OTHER")) gotoexit; break;
}
/** * pm80xx_chip_soft_rst - soft reset the PM8001 chip, so that all * FW register status are reset to the originated status. * @pm8001_ha: our hba card information
*/
/* Process MPI table uninitialization only if FW is ready */ if (!pm8001_ha->controller_fatal_error) { /* Check if MPI is in ready state to reset */ if (mpi_uninit_check(pm8001_ha) != 0) {
u32 r0 = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_0);
u32 r1 = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_1);
u32 r2 = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_2);
u32 r3 = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_3);
pm8001_dbg(pm8001_ha, FAIL, "MPI state is not ready scratch: %x:%x:%x:%x\n",
r0, r1, r2, r3); /* if things aren't ready but the bootloader is ok then * try the reset anyway.
*/ if (r1 & SCRATCH_PAD1_BOOTSTATE_MASK) return -1;
}
} /* checked for reset register normal state; 0x0 */
regval = pm8001_cr32(pm8001_ha, 0, SPC_REG_SOFT_RESET);
pm8001_dbg(pm8001_ha, INIT, "reset register before write : 0x%x\n",
regval);
/** * mpi_ssp_completion - process the event that FW response to the SSP request. * @pm8001_ha: our hba card information * @piomb: the message contents of this outbound message. * * When FW has completed a ssp request for example a IO request, after it has * filled the SG data with the data, it will trigger this event representing * that he has finished the job; please check the corresponding buffer. * So we will tell the caller who maybe waiting the result to tell upper layer * that the task has been finished.
*/ staticvoid
mpi_ssp_completion(struct pm8001_hba_info *pm8001_ha, void *piomb)
{ struct sas_task *t; struct pm8001_ccb_info *ccb; unsignedlong flags;
u32 status;
u32 param;
u32 tag; struct ssp_completion_resp *psspPayload; struct task_status_struct *ts; struct ssp_response_iu *iu; struct pm8001_device *pm8001_dev;
psspPayload = (struct ssp_completion_resp *)(piomb + 4);
status = le32_to_cpu(psspPayload->status);
tag = le32_to_cpu(psspPayload->tag);
ccb = &pm8001_ha->ccb_info[tag]; if ((status == IO_ABORTED) && ccb->open_retry) { /* Being completed by another */
ccb->open_retry = 0; return;
}
pm8001_dev = ccb->device;
param = le32_to_cpu(psspPayload->param);
t = ccb->task;
if (status && status != IO_UNDERFLOW)
pm8001_dbg(pm8001_ha, FAIL, "sas IO status 0x%x\n", status); if (unlikely(!t || !t->lldd_task || !t->dev)) return;
ts = &t->task_status;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.