diff options
-rw-r--r-- | drivers/scsi/isci/Makefile | 2 | ||||
-rw-r--r-- | drivers/scsi/isci/core/scic_config_parameters.h | 41 | ||||
-rw-r--r-- | drivers/scsi/isci/core/scic_sds_controller.c | 6 | ||||
-rw-r--r-- | drivers/scsi/isci/firmware/create_fw.c | 197 | ||||
-rw-r--r-- | drivers/scsi/isci/firmware/create_fw.h | 67 | ||||
-rw-r--r-- | drivers/scsi/isci/host.c | 47 | ||||
-rw-r--r-- | drivers/scsi/isci/host.h | 2 | ||||
-rw-r--r-- | drivers/scsi/isci/init.c | 191 | ||||
-rw-r--r-- | drivers/scsi/isci/isci.h | 39 | ||||
-rw-r--r-- | drivers/scsi/isci/probe_roms.c | 133 | ||||
-rw-r--r-- | drivers/scsi/isci/probe_roms.h | 130 | ||||
-rw-r--r-- | firmware/isci/isci_firmware.bin.ihex | 25 |
12 files changed, 453 insertions, 427 deletions
diff --git a/drivers/scsi/isci/Makefile b/drivers/scsi/isci/Makefile index d402d679a316..1252d768c42d 100644 --- a/drivers/scsi/isci/Makefile +++ b/drivers/scsi/isci/Makefile @@ -9,7 +9,7 @@ EXTRA_CFLAGS += -Idrivers/scsi/isci/core/ -Idrivers/scsi/isci/ obj-$(CONFIG_SCSI_ISCI) += isci.o isci-objs := init.o phy.o request.o sata.o \ remote_device.o port.o timers.o \ - host.o task.o events.o \ + host.o task.o events.o probe_roms.o \ core/scic_sds_controller.o \ core/scic_sds_remote_device.o \ core/scic_sds_request.o \ diff --git a/drivers/scsi/isci/core/scic_config_parameters.h b/drivers/scsi/isci/core/scic_config_parameters.h index 485fefc08883..5e1345daf014 100644 --- a/drivers/scsi/isci/core/scic_config_parameters.h +++ b/drivers/scsi/isci/core/scic_config_parameters.h @@ -68,6 +68,7 @@ #include "sci_status.h" #include "intel_sas.h" #include "sci_controller_constants.h" +#include "probe_roms.h" struct scic_sds_controller; @@ -224,44 +225,6 @@ union scic_user_parameters { #define SCIC_SDS_PARM_PHY_MASK_MAX 0xF /** - * struct scic_sds_oem_parameters - This structure delineates the various OEM - * parameters that must be set the core user. - * - * - */ -struct scic_sds_oem_parameters { - struct { - /** - * This field indicates whether Spread Spectrum Clocking (SSC) - * should be enabled or disabled. - */ - bool do_enable_ssc; - - } controller; - - struct { - /** - * This field specifies the phys to be contained inside a port. - * The bit position in the mask specifies the index of the phy - * to be contained in the port. Multiple bits (i.e. phys) - * can be contained in a single port. - */ - u8 phy_mask; - - } ports[SCI_MAX_PORTS]; - - struct sci_phy_oem_params { - /** - * This field specifies the SAS address to be transmitted on - * for this phy index. - */ - struct sci_sas_address sas_address; - - } phys[SCI_MAX_PHYS]; - -}; - -/** * This structure/union specifies the various different OEM parameter sets * available. Each type is specific to a hardware controller version. * @@ -273,7 +236,7 @@ union scic_oem_parameters { * Storage Controller Unit (SCU) Driver Standard (SDS) version * 1. */ - struct scic_sds_oem_parameters sds1; + struct scic_sds_oem_params sds1; }; diff --git a/drivers/scsi/isci/core/scic_sds_controller.c b/drivers/scsi/isci/core/scic_sds_controller.c index 799a04bc0938..e7f3711b4afc 100644 --- a/drivers/scsi/isci/core/scic_sds_controller.c +++ b/drivers/scsi/isci/core/scic_sds_controller.c @@ -2039,10 +2039,8 @@ static void scic_sds_controller_set_default_config_parameters(struct scic_sds_co /* Initialize all of the phy parameter information. */ for (index = 0; index < SCI_MAX_PHYS; index++) { - /* - * Default to 3G (i.e. Gen 2) for now. User can override if - * they choose. */ - scic->user_parameters.sds1.phys[index].max_speed_generation = 2; + /* Default to 6G (i.e. Gen 3) for now. */ + scic->user_parameters.sds1.phys[index].max_speed_generation = 3; /* the frequencies cannot be 0 */ scic->user_parameters.sds1.phys[index].align_insertion_frequency = 0x7f; diff --git a/drivers/scsi/isci/firmware/create_fw.c b/drivers/scsi/isci/firmware/create_fw.c index 442caac9675d..f8f96d6eb7df 100644 --- a/drivers/scsi/isci/firmware/create_fw.c +++ b/drivers/scsi/isci/firmware/create_fw.c @@ -6,157 +6,30 @@ #include <fcntl.h> #include <string.h> #include <errno.h> +#include <asm/types.h> +#include <strings.h> +#include <stdint.h> -char blob_name[] = "isci_firmware.bin"; -char id[] = "#SCU MAGIC#"; -unsigned char version = 1; -unsigned char sub_version = 0; - - -/* - * For all defined arrays: - * elements 0-3 are for SCU0, ports 0-3 - * elements 4-7 are for SCU1, ports 0-3 - * - * valid configurations for one SCU are: - * P0 P1 P2 P3 - * ---------------- - * 0xF,0x0,0x0,0x0 # 1 x4 port - * 0x3,0x0,0x4,0x8 # Phys 0 and 1 are a x2 port, phy 2 and phy 3 are each x1 - * # ports - * 0x1,0x2,0xC,0x0 # Phys 0 and 1 are each x1 ports, phy 2 and phy 3 are a x2 - * # port - * 0x3,0x0,0xC,0x0 # Phys 0 and 1 are a x2 port, phy 2 and phy 3 are a x2 port - * 0x1,0x2,0x4,0x8 # Each phy is a x1 port (this is the default configuration) - * - * if there is a port/phy on which you do not wish to override the default - * values, use the value assigned to UNINIT_PARAM (255). - */ -unsigned int phy_mask[] = { 1, 2, 4, 8, 1, 2, 4, 8 }; - - -/* denotes SAS generation. i.e. 3: SAS Gen 3 6G */ -unsigned int phy_gen[] = { 3, 3, 3, 3, 3, 3, 3, 3 }; - -/* - * if there is a port/phy on which you do not wish to override the default - * values, use the value "0000000000000000". SAS address of zero's is - * considered invalid and will not be used. - */ -unsigned long long sas_addr[] = { 0x5FCFFFFFF0000000ULL, - 0x5FCFFFFFF1000000ULL, - 0x5FCFFFFFF2000000ULL, - 0x5FCFFFFFF3000000ULL, - 0x5FCFFFFFF4000000ULL, - 0x5FCFFFFFF5000000ULL, - 0x5FCFFFFFF6000000ULL, - 0x5FCFFFFFF7000000ULL }; - -int write_blob(void) +#include "create_fw.h" +#include "../probe_roms.h" + +int write_blob(struct isci_orom *isci_orom) { FILE *fd; int err; + size_t count; fd = fopen(blob_name, "w+"); if (!fd) { perror("Open file for write failed"); + fclose(fd); return -EIO; } - /* write id */ - err = fwrite((void *)id, sizeof(char), strlen(id)+1, fd); - if (err == 0) { - perror("write id failed"); - return err; - } - - /* write version */ - err = fwrite((void *)&version, sizeof(version), 1, fd); - if (err == 0) { - perror("write version failed"); - return err; - } - - /* write sub version */ - err = fwrite((void *)&sub_version, sizeof(sub_version), 1, fd); - if (err == 0) { - perror("write subversion failed"); - return err; - } - - /* write phy mask header */ - err = fputc(0x1, fd); - if (err == EOF) { - perror("write phy mask header failed"); - return -EIO; - } - - /* write size */ - err = fputc(8, fd); - if (err == EOF) { - perror("write phy mask size failed"); - return -EIO; - } - - /* write phy masks */ - err = fwrite((void *)phy_mask, 1, sizeof(phy_mask), fd); - if (err == 0) { - perror("write phy_mask failed"); - return err; - } - - /* write phy gen header */ - err = fputc(0x2, fd); - if (err == EOF) { - perror("write phy gen header failed"); - return -EIO; - } - - /* write size */ - err = fputc(8, fd); - if (err == EOF) { - perror("write phy gen size failed"); - return -EIO; - } - - /* write phy_gen */ - err = fwrite((void *)phy_gen, - 1, - sizeof(phy_gen), - fd); - if (err == 0) { - perror("write phy_gen failed"); - return err; - } - - /* write phy gen header */ - err = fputc(0x3, fd); - if (err == EOF) { - perror("write sas addr header failed"); - return -EIO; - } - - /* write size */ - err = fputc(8, fd); - if (err == EOF) { - perror("write sas addr size failed"); - return -EIO; - } - - /* write sas_addr */ - err = fwrite((void *)sas_addr, - 1, - sizeof(sas_addr), - fd); - if (err == 0) { - perror("write sas_addr failed"); - return err; - } - - /* write end header */ - err = fputc(0xff, fd); - if (err == EOF) { - perror("write end header failed"); + count = fwrite(isci_orom, sizeof(struct isci_orom), 1, fd); + if (count != 1) { + perror("Write data failed"); + fclose(fd); return -EIO; } @@ -165,13 +38,53 @@ int write_blob(void) return 0; } +void set_binary_values(struct isci_orom *isci_orom) +{ + int ctrl_idx, phy_idx, port_idx; + + /* setting OROM signature */ + strncpy(isci_orom->hdr.signature, sig, strlen(sig)); + isci_orom->hdr.version = 0x10; + isci_orom->hdr.total_block_length = sizeof(struct isci_orom); + isci_orom->hdr.hdr_length = sizeof(struct sci_bios_oem_param_block_hdr); + isci_orom->hdr.num_elements = num_elements; + + for (ctrl_idx = 0; ctrl_idx < 2; ctrl_idx++) { + isci_orom->ctrl[ctrl_idx].controller.mode_type = mode_type; + isci_orom->ctrl[ctrl_idx].controller.max_concurrent_dev_spin_up = + max_num_concurrent_dev_spin_up; + isci_orom->ctrl[ctrl_idx].controller.do_enable_ssc = + enable_ssc; + + for (port_idx = 0; port_idx < 4; port_idx++) + isci_orom->ctrl[ctrl_idx].ports[port_idx].phy_mask = + phy_mask[ctrl_idx][port_idx]; + + for (phy_idx = 0; phy_idx < 4; phy_idx++) { + isci_orom->ctrl[ctrl_idx].phys[phy_idx].sas_address.high = + (__u32)(sas_addr[ctrl_idx][phy_idx] >> 32); + isci_orom->ctrl[ctrl_idx].phys[phy_idx].sas_address.low = + (__u32)(sas_addr[ctrl_idx][phy_idx]); + } + } +} + int main(void) { int err; + struct isci_orom *isci_orom; + + isci_orom = malloc(sizeof(struct isci_orom)); + memset(isci_orom, 0, sizeof(struct isci_orom)); - err = write_blob(); - if (err < 0) + set_binary_values(isci_orom); + + err = write_blob(isci_orom); + if (err < 0) { + free(isci_orom); return err; + } + free(isci_orom); return 0; } diff --git a/drivers/scsi/isci/firmware/create_fw.h b/drivers/scsi/isci/firmware/create_fw.h new file mode 100644 index 000000000000..bedbe4fad181 --- /dev/null +++ b/drivers/scsi/isci/firmware/create_fw.h @@ -0,0 +1,67 @@ +#ifndef _CREATE_FW_H_ +#define _CREATE_FW_H_ + + +/* we are configuring for 2 SCUs */ +static const int num_elements = 2; + +/* + * For all defined arrays: + * elements 0-3 are for SCU0, ports 0-3 + * elements 4-7 are for SCU1, ports 0-3 + * + * valid configurations for one SCU are: + * P0 P1 P2 P3 + * ---------------- + * 0xF,0x0,0x0,0x0 # 1 x4 port + * 0x3,0x0,0x4,0x8 # Phys 0 and 1 are a x2 port, phy 2 and phy 3 are each x1 + * # ports + * 0x1,0x2,0xC,0x0 # Phys 0 and 1 are each x1 ports, phy 2 and phy 3 are a x2 + * # port + * 0x3,0x0,0xC,0x0 # Phys 0 and 1 are a x2 port, phy 2 and phy 3 are a x2 port + * 0x1,0x2,0x4,0x8 # Each phy is a x1 port (this is the default configuration) + * + * if there is a port/phy on which you do not wish to override the default + * values, use the value assigned to UNINIT_PARAM (255). + */ +#ifdef MPC +static const __u8 phy_mask[2][4] = { {1, 2, 4, 8}, + {1, 2, 4, 8} }; +#else /* APC (default) */ +static const __u8 phy_mask[2][4]; +#endif + +/* discovery mode type (port auto config mode by default ) */ +static const int mode_type; + +/* Maximum number of concurrent device spin up */ +static const int max_num_concurrent_dev_spin_up = 1; + +/* enable of ssc operation */ +static const int enable_ssc; + +/* AFE_TX_AMP_CONTROL */ +static const unsigned int afe_tx_amp_control0 = 0x000e7c03; +static const unsigned int afe_tx_amp_control1 = 0x000e7c03; +static const unsigned int afe_tx_amp_control2 = 0x000e7c03; +static const unsigned int afe_tx_amp_control3 = 0x000e7c03; + +/* + * if there is a port/phy on which you do not wish to override the default + * values, use the value "0000000000000000". SAS address of zero's is + * considered invalid and will not be used. + */ +static const unsigned long long sas_addr[2][4] = { { 0x5FCFFFFFF0000000ULL, + 0x5FCFFFFFF1000000ULL, + 0x5FCFFFFFF2000000ULL, + 0x5FCFFFFFF3000000ULL }, + { 0x5FCFFFFFF4000000ULL, + 0x5FCFFFFFF5000000ULL, + 0x5FCFFFFFF6000000ULL, + 0x5FCFFFFFF7000000ULL } }; + +static const char blob_name[] = "isci_firmware.bin"; +static const char sig[] = "ISCUOEMB"; +static const unsigned char version = 1; + +#endif diff --git a/drivers/scsi/isci/host.c b/drivers/scsi/isci/host.c index dc231c22ea93..bb5b54d361b0 100644 --- a/drivers/scsi/isci/host.c +++ b/drivers/scsi/isci/host.c @@ -61,6 +61,7 @@ #include "port.h" #include "request.h" #include "host.h" +#include "probe_roms.h" irqreturn_t isci_msix_isr(int vec, void *data) { @@ -419,6 +420,7 @@ int isci_host_init(struct isci_host *isci_host) struct scic_sds_controller *controller; union scic_oem_parameters scic_oem_params; union scic_user_parameters scic_user_params; + struct isci_pci_info *pci_info = to_pci_info(isci_host->pdev); isci_timer_list_construct(isci_host); @@ -461,31 +463,32 @@ int isci_host_init(struct isci_host *isci_host) sci_object_set_association(isci_host->core_controller, (void *)isci_host); - /* grab initial values stored in the controller object for OEM and USER - * parameters */ - scic_oem_parameters_get(controller, &scic_oem_params); + /* + * grab initial values stored in the controller object for OEM and USER + * parameters + */ scic_user_parameters_get(controller, &scic_user_params); + status = scic_user_parameters_set(isci_host->core_controller, + &scic_user_params); + if (status != SCI_SUCCESS) { + dev_warn(&isci_host->pdev->dev, + "%s: scic_user_parameters_set failed\n", + __func__); + return -ENODEV; + } + + scic_oem_parameters_get(controller, &scic_oem_params); - if (isci_firmware) { - /* grab any OEM and USER parameters specified in binary blob */ + /* grab any OEM parameters specified in orom */ + if (pci_info->orom) { status = isci_parse_oem_parameters(&scic_oem_params, - isci_host->id, - isci_firmware); + pci_info->orom, + isci_host->id); if (status != SCI_SUCCESS) { dev_warn(&isci_host->pdev->dev, "parsing firmware oem parameters failed\n"); return -EINVAL; } - - status = isci_parse_user_parameters(&scic_user_params, - isci_host->id, - isci_firmware); - if (status != SCI_SUCCESS) { - dev_warn(&isci_host->pdev->dev, - "%s: isci_parse_user_parameters" - " failed\n", __func__); - return -EINVAL; - } } else { status = scic_oem_parameters_set(isci_host->core_controller, &scic_oem_params); @@ -495,16 +498,6 @@ int isci_host_init(struct isci_host *isci_host) __func__); return -ENODEV; } - - - status = scic_user_parameters_set(isci_host->core_controller, - &scic_user_params); - if (status != SCI_SUCCESS) { - dev_warn(&isci_host->pdev->dev, - "%s: scic_user_parameters_set failed\n", - __func__); - return -ENODEV; - } } tasklet_init(&isci_host->completion_tasklet, diff --git a/drivers/scsi/isci/host.h b/drivers/scsi/isci/host.h index 889a7850255a..d012b69d8d61 100644 --- a/drivers/scsi/isci/host.h +++ b/drivers/scsi/isci/host.h @@ -140,8 +140,8 @@ static inline struct isci_remote_device *idev_by_id(struct isci_host *ihost, int struct isci_pci_info { struct msix_entry msix_entries[SCI_MAX_MSIX_INT]; - int core_lib_array_index; struct isci_host *hosts[SCI_MAX_CONTROLLERS]; + struct isci_orom *orom; }; static inline struct isci_pci_info *to_pci_info(struct pci_dev *pdev) diff --git a/drivers/scsi/isci/init.c b/drivers/scsi/isci/init.c index 3f2bb137bcb1..65519321e1cc 100644 --- a/drivers/scsi/isci/init.c +++ b/drivers/scsi/isci/init.c @@ -56,12 +56,15 @@ #include <linux/kernel.h> #include <linux/init.h> #include <linux/module.h> +#include <linux/firmware.h> +#include <linux/efi.h> #include <asm/string.h> #include "isci.h" #include "task.h" #include "sci_controller_constants.h" #include "scic_remote_device.h" #include "sci_environment.h" +#include "probe_roms.h" static struct scsi_transport_template *isci_transport_template; @@ -373,85 +376,6 @@ static int isci_setup_interrupts(struct pci_dev *pdev) return err; } -/** - * isci_parse_oem_parameters() - This method will take OEM parameters - * from the module init parameters and copy them to oem_params. This will - * only copy values that are not set to the module parameter default values - * @oem_parameters: This parameter specifies the controller default OEM - * parameters. It is expected that this has been initialized to the default - * parameters for the controller - * - * - */ -enum sci_status isci_parse_oem_parameters(union scic_oem_parameters *oem_params, - int scu_index, - struct isci_firmware *fw) -{ - int i; - - /* check for valid inputs */ - if (!(scu_index >= 0 - && scu_index < SCI_MAX_CONTROLLERS - && oem_params != NULL)) { - return SCI_FAILURE; - } - - for (i = 0; i < SCI_MAX_PHYS; i++) { - int array_idx = i + (SCI_MAX_PHYS * scu_index); - u64 sas_addr = fw->sas_addrs[array_idx]; - - if (sas_addr != 0) { - oem_params->sds1.phys[i].sas_address.low = - (u32)(sas_addr & 0xffffffff); - oem_params->sds1.phys[i].sas_address.high = - (u32)((sas_addr >> 32) & 0xffffffff); - } - } - - for (i = 0; i < SCI_MAX_PORTS; i++) { - int array_idx = i + (SCI_MAX_PORTS * scu_index); - u32 pmask = fw->phy_masks[array_idx]; - - oem_params->sds1.ports[i].phy_mask = pmask; - } - - return SCI_SUCCESS; -} - -/** - * isci_parse_user_parameters() - This method will take user parameters - * from the module init parameters and copy them to user_params. This will - * only copy values that are not set to the module parameter default values - * @user_parameters: This parameter specifies the controller default user - * parameters. It is expected that this has been initialized to the default - * parameters for the controller - * - * - */ -enum sci_status isci_parse_user_parameters( - union scic_user_parameters *user_params, - int scu_index, - struct isci_firmware *fw) -{ - int i; - - if (!(scu_index >= 0 - && scu_index < SCI_MAX_CONTROLLERS - && user_params != NULL)) { - return SCI_FAILURE; - } - - for (i = 0; i < SCI_MAX_PORTS; i++) { - int array_idx = i + (SCI_MAX_PORTS * scu_index); - u32 gen = fw->phy_gens[array_idx]; - - user_params->sds1.phys[i].max_speed_generation = gen; - - } - - return SCI_SUCCESS; -} - static struct isci_host *isci_host_alloc(struct pci_dev *pdev, int id) { struct isci_host *isci_host; @@ -535,73 +459,13 @@ static void check_si_rev(struct pci_dev *pdev) } -static int isci_verify_firmware(const struct firmware *fw, - struct isci_firmware *isci_fw) -{ - const u8 *tmp; - - if (fw->size < ISCI_FIRMWARE_MIN_SIZE) - return -EINVAL; - - tmp = fw->data; - - /* 12th char should be the NULL terminate for the ID string */ - if (tmp[11] != '\0') - return -EINVAL; - - if (strncmp("#SCU MAGIC#", tmp, 11) != 0) - return -EINVAL; - - isci_fw->id = tmp; - isci_fw->version = fw->data[ISCI_FW_VER_OFS]; - isci_fw->subversion = fw->data[ISCI_FW_SUBVER_OFS]; - - tmp = fw->data + ISCI_FW_DATA_OFS; - - while (*tmp != ISCI_FW_HDR_EOF) { - switch (*tmp) { - case ISCI_FW_HDR_PHYMASK: - tmp++; - isci_fw->phy_masks_size = *tmp; - tmp++; - isci_fw->phy_masks = (const u32 *)tmp; - tmp += sizeof(u32) * isci_fw->phy_masks_size; - break; - - case ISCI_FW_HDR_PHYGEN: - tmp++; - isci_fw->phy_gens_size = *tmp; - tmp++; - isci_fw->phy_gens = (const u32 *)tmp; - tmp += sizeof(u32) * isci_fw->phy_gens_size; - break; - - case ISCI_FW_HDR_SASADDR: - tmp++; - isci_fw->sas_addrs_size = *tmp; - tmp++; - isci_fw->sas_addrs = (const u64 *)tmp; - tmp += sizeof(u64) * isci_fw->sas_addrs_size; - break; - - default: - pr_err("bad field in firmware binary blob\n"); - return -EINVAL; - } - } - - pr_info("isci firmware v%u.%u loaded.\n", - isci_fw->version, isci_fw->subversion); - - return SCI_SUCCESS; -} - static int __devinit isci_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) { struct isci_pci_info *pci_info; int err, i; struct isci_host *isci_host; const struct firmware *fw = NULL; + struct isci_orom *orom; check_si_rev(pdev); @@ -610,33 +474,32 @@ static int __devinit isci_pci_probe(struct pci_dev *pdev, const struct pci_devic return -ENOMEM; pci_set_drvdata(pdev, pci_info); - err = request_firmware(&fw, ISCI_FW_NAME, &pdev->dev); - if (err) { - dev_warn(&pdev->dev, - "Loading firmware failed, using default values\n"); - dev_warn(&pdev->dev, - "Default OEM configuration being used:" - " 4 narrow ports, and default SAS Addresses\n"); - } else { - isci_firmware = devm_kzalloc(&pdev->dev, - sizeof(struct isci_firmware), - GFP_KERNEL); - if (isci_firmware) { - err = isci_verify_firmware(fw, isci_firmware); - if (err != SCI_SUCCESS) { - dev_warn(&pdev->dev, - "firmware verification failed\n"); - dev_warn(&pdev->dev, - "Default OEM configuration being used:" - " 4 narrow ports, and default SAS " - "Addresses\n"); - devm_kfree(&pdev->dev, isci_firmware); - isci_firmware = NULL; - } + if (efi_enabled) { + /* do EFI parsing here */ + orom = NULL; + } else + orom = isci_request_oprom(pdev); + + if (!orom) { + orom = isci_request_firmware(pdev, fw); + if (!orom) { + /* TODO convert this to WARN_TAINT_ONCE once the + * orom/efi parameter support is widely available + */ + dev_warn(&pdev->dev, + "Loading user firmware failed, using default " + "values\n"); + dev_warn(&pdev->dev, + "Default OEM configuration being used: 4 " + "narrow ports, and default SAS Addresses\n"); } - release_firmware(fw); } + if (orom) + dev_info(&pdev->dev, "sas parameters (version: %#x) loaded\n", + orom->hdr.version); + pci_info->orom = orom; + err = isci_pci_init(pdev); if (err) return err; diff --git a/drivers/scsi/isci/isci.h b/drivers/scsi/isci/isci.h index b3f63f1a46cf..83422d43c1d3 100644 --- a/drivers/scsi/isci/isci.h +++ b/drivers/scsi/isci/isci.h @@ -61,7 +61,6 @@ #include <linux/types.h> #include <linux/spinlock.h> #include <linux/interrupt.h> -#include <linux/firmware.h> #include <linux/bug.h> #include <scsi/libsas.h> #include <scsi/scsi.h> @@ -76,34 +75,6 @@ #include "task.h" #include "sata.h" -extern struct isci_firmware *isci_firmware; - -#define ISCI_FW_NAME "isci/isci_firmware.bin" - -#define ISCI_FIRMWARE_MIN_SIZE 149 - -#define ISCI_FW_IDSIZE 12 -#define ISCI_FW_VER_OFS ISCI_FW_IDSIZE -#define ISCI_FW_SUBVER_OFS ISCI_FW_VER_OFS + 1 -#define ISCI_FW_DATA_OFS ISCI_FW_SUBVER_OFS + 1 - -#define ISCI_FW_HDR_PHYMASK 0x1 -#define ISCI_FW_HDR_PHYGEN 0x2 -#define ISCI_FW_HDR_SASADDR 0x3 -#define ISCI_FW_HDR_EOF 0xff - -struct isci_firmware { - const u8 *id; - u8 version; - u8 subversion; - const u32 *phy_masks; - u8 phy_masks_size; - const u32 *phy_gens; - u8 phy_gens_size; - const u64 *sas_addrs; - u8 sas_addrs_size; -}; - irqreturn_t isci_msix_isr(int vec, void *data); irqreturn_t isci_intx_isr(int vec, void *data); irqreturn_t isci_error_isr(int vec, void *data); @@ -113,14 +84,4 @@ void scic_sds_controller_completion_handler(struct scic_sds_controller *scic); bool scic_sds_controller_error_isr(struct scic_sds_controller *scic); void scic_sds_controller_error_handler(struct scic_sds_controller *scic); -enum sci_status isci_parse_oem_parameters( - union scic_oem_parameters *oem_params, - int scu_index, - struct isci_firmware *fw); - -enum sci_status isci_parse_user_parameters( - union scic_user_parameters *user_params, - int scu_index, - struct isci_firmware *fw); - #endif /* __ISCI_H__ */ diff --git a/drivers/scsi/isci/probe_roms.c b/drivers/scsi/isci/probe_roms.c new file mode 100644 index 000000000000..0b90e7c546c0 --- /dev/null +++ b/drivers/scsi/isci/probe_roms.c @@ -0,0 +1,133 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + */ + +/* probe_roms - scan for oem parameters */ + +#include <linux/kernel.h> +#include <linux/firmware.h> +#include <linux/uaccess.h> +#include <asm/probe_roms.h> + +#include "isci.h" +#include "task.h" +#include "sci_controller_constants.h" +#include "scic_remote_device.h" +#include "sci_environment.h" +#include "probe_roms.h" + +struct isci_orom *isci_request_oprom(struct pci_dev *pdev) +{ + void __iomem *oprom = pci_map_biosrom(pdev); + struct isci_orom *rom = NULL; + size_t len, i; + + if (!oprom) + return NULL; + + len = pci_biosrom_size(pdev); + rom = devm_kzalloc(&pdev->dev, sizeof(*rom), GFP_KERNEL); + + for (i = 0; i < len && rom; i += ISCI_ROM_SIG_SIZE) { + memcpy_fromio(rom->hdr.signature, oprom + i, ISCI_ROM_SIG_SIZE); + if (memcmp(rom->hdr.signature, ISCI_ROM_SIG, + ISCI_ROM_SIG_SIZE) == 0) { + size_t copy_len = min(len - i, sizeof(*rom)); + + memcpy_fromio(rom, oprom + i, copy_len); + break; + } + } + + if (i >= len) { + dev_err(&pdev->dev, "oprom parse error\n"); + devm_kfree(&pdev->dev, rom); + rom = NULL; + } + pci_unmap_biosrom(oprom); + + return rom; +} + +/** + * isci_parse_oem_parameters() - This method will take OEM parameters + * from the module init parameters and copy them to oem_params. This will + * only copy values that are not set to the module parameter default values + * @oem_parameters: This parameter specifies the controller default OEM + * parameters. It is expected that this has been initialized to the default + * parameters for the controller + * + * + */ +enum sci_status isci_parse_oem_parameters(union scic_oem_parameters *oem_params, + struct isci_orom *orom, int scu_index) +{ + int i; + + /* check for valid inputs */ + if (!(scu_index >= 0 + && scu_index < SCI_MAX_CONTROLLERS + && oem_params != NULL)) + return -EINVAL; + + for (i = 0; i < SCI_MAX_PHYS; i++) { + oem_params->sds1.phys[i].sas_address.low = + orom->ctrl[scu_index].phys[i].sas_address.low; + oem_params->sds1.phys[i].sas_address.high = + orom->ctrl[scu_index].phys[i].sas_address.high; + } + + for (i = 0; i < SCI_MAX_PORTS; i++) + oem_params->sds1.ports[i].phy_mask = + orom->ctrl[scu_index].ports[i].phy_mask; + + return 0; +} + +struct isci_orom *isci_request_firmware(struct pci_dev *pdev, const struct firmware *fw) +{ + struct isci_orom *orom = NULL, *data; + + if (request_firmware(&fw, ISCI_FW_NAME, &pdev->dev) != 0) + return NULL; + + if (fw->size < sizeof(*orom)) + goto out; + + data = (struct isci_orom *)fw->data; + + if (strncmp(ISCI_ROM_SIG, data->hdr.signature, + strlen(ISCI_ROM_SIG)) != 0) + goto out; + + orom = devm_kzalloc(&pdev->dev, fw->size, GFP_KERNEL); + if (!orom) + goto out; + + memcpy(orom, fw->data, fw->size); + + out: + release_firmware(fw); + + return orom; +} diff --git a/drivers/scsi/isci/probe_roms.h b/drivers/scsi/isci/probe_roms.h new file mode 100644 index 000000000000..76651c01895a --- /dev/null +++ b/drivers/scsi/isci/probe_roms.h @@ -0,0 +1,130 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 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 MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * 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 DAMAGE. + */ +#ifndef _ISCI_PROBE_ROMS_H_ +#define _ISCI_PROBE_ROMS_H_ + +#ifdef __KERNEL__ +#include <linux/firmware.h> +#include <linux/pci.h> + +struct isci_orom *isci_request_oprom(struct pci_dev *pdev); + +union scic_oem_parameters; +struct isci_orom; + +enum sci_status isci_parse_oem_parameters( + union scic_oem_parameters *oem_params, + struct isci_orom *orom, + int scu_index); +struct isci_orom *isci_request_firmware(struct pci_dev *pdev, const struct firmware *fw); +int isci_get_efi_var(struct pci_dev *pdev); +#else +#define SCI_MAX_PORTS 4 +#define SCI_MAX_PHYS 4 +#endif + +#define ISCI_FW_NAME "isci/isci_firmware.bin" + +#define ROMSIGNATURE 0xaa55 + +#define ISCI_ROM_SIG "ISCUOEMB" +#define ISCI_ROM_SIG_SIZE 8 + +#define ISCI_EFI_VENDOR_GUID NULL_GUID +#define ISCI_EFI_ATTRIBUTES 0 +#define ISCI_EFI_VAR_NAME "isci_oemb" + +struct sci_bios_oem_param_block_hdr { + uint8_t signature[ISCI_ROM_SIG_SIZE]; + uint16_t total_block_length; + uint8_t hdr_length; + uint8_t version; + uint8_t preboot_source; + uint8_t num_elements; + uint8_t element_length; + uint8_t reserved[8]; +} __attribute__ ((packed)); + +struct scic_sds_oem_params { + struct { + uint8_t mode_type; + uint8_t max_concurrent_dev_spin_up; + uint8_t do_enable_ssc; + uint8_t reserved; + } controller; + + struct { + uint8_t phy_mask; + } ports[SCI_MAX_PORTS]; + + struct sci_phy_oem_params { + struct { + uint32_t high; + uint32_t low; + } sas_address; + + uint32_t afe_tx_amp_control0; + uint32_t afe_tx_amp_control1; + uint32_t afe_tx_amp_control2; + uint32_t afe_tx_amp_control3; + } phys[SCI_MAX_PHYS]; +} __attribute__ ((packed)); + +struct isci_orom { + struct sci_bios_oem_param_block_hdr hdr; + struct scic_sds_oem_params ctrl[2]; +} __attribute__ ((packed)); + +#endif diff --git a/firmware/isci/isci_firmware.bin.ihex b/firmware/isci/isci_firmware.bin.ihex index 9fc9e60f399c..7f12b39e7910 100644 --- a/firmware/isci/isci_firmware.bin.ihex +++ b/firmware/isci/isci_firmware.bin.ihex @@ -1,11 +1,16 @@ -:1000000023534355204D4147494323000100010834 -:1000100001000000020000000400000008000000D1 -:1000200001000000020000000400000008000000C1 -:1000300002080300000003000000030000000300AA -:1000400000000300000003000000030000000300A4 -:1000500000000308000000F0FFFFCF5F000000F188 -:10006000FFFFCF5F000000F2FFFFCF5F000000F353 -:10007000FFFFCF5F000000F4FFFFCF5F000000F53F -:10008000FFFFCF5F000000F6FFFFCF5F000000F72B -:05009000FFFFCF5FFF40 +:10000000495343554F454D42E70017100002000089 +:10001000000000000000000001000000000000FFE0 +:10002000FFCF5F000000F0000000000000000000B3 +:1000300000000000000000FFFFCF5F000000F100A3 +:10004000000000000000000000000000000000FFB1 +:10005000FFCF5F000000F200000000000000000081 +:1000600000000000000000FFFFCF5F000000F30071 +:100070000000000000000000000000000000000080 +:1000800001000000000000FFFFCF5F000000F4004F +:10009000000000000000000000000000000000FF61 +:1000A000FFCF5F000000F50000000000000000002E +:1000B00000000000000000FFFFCF5F000000F6001E +:1000C000000000000000000000000000000000FF31 +:1000D000FFCF5F000000F7000000000000000000FC +:0700E0000000000000000019 :00000001FF |