diff options
author | Dan Williams <dan.j.williams@intel.com> | 2011-03-08 20:52:49 +0300 |
---|---|---|
committer | Dan Williams <dan.j.williams@intel.com> | 2011-07-03 14:55:30 +0400 |
commit | d044af17aacd03a1f4fced1af4b7570d205c8fd9 (patch) | |
tree | 1304fd0c7fa979fb229a4bf57771e9e6cde7b37d | |
parent | 9affa289e2f9ef4721e85edbde86466524bfe957 (diff) | |
download | linux-d044af17aacd03a1f4fced1af4b7570d205c8fd9.tar.xz |
isci: Add support for probing OROM for OEM params
We need to scan the OROM for signature and grab the OEM parameters. We
also need to do the same for EFI. If all fails then we resort to user
binary blob, and if that fails then we go to the defaults.
Share the format with the create_fw utility so that all possible sources
of the parameters are in-sync.
Signed-off-by: Dave Jiang <dave.jiang@intel.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
-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 |