diff options
Diffstat (limited to 'drivers/scsi/isci/phy.c')
-rw-r--r-- | drivers/scsi/isci/phy.c | 172 |
1 files changed, 111 insertions, 61 deletions
diff --git a/drivers/scsi/isci/phy.c b/drivers/scsi/isci/phy.c index 35f50c2183e1..fe18acfd6eb3 100644 --- a/drivers/scsi/isci/phy.c +++ b/drivers/scsi/isci/phy.c @@ -91,22 +91,23 @@ sci_phy_transport_layer_initialization(struct isci_phy *iphy, static enum sci_status sci_phy_link_layer_initialization(struct isci_phy *iphy, - struct scu_link_layer_registers __iomem *reg) + struct scu_link_layer_registers __iomem *llr) { struct isci_host *ihost = iphy->owning_port->owning_controller; + struct sci_phy_user_params *phy_user; + struct sci_phy_oem_params *phy_oem; int phy_idx = iphy->phy_index; - struct sci_phy_user_params *phy_user = &ihost->user_parameters.phys[phy_idx]; - struct sci_phy_oem_params *phy_oem = - &ihost->oem_parameters.phys[phy_idx]; - u32 phy_configuration; struct sci_phy_cap phy_cap; + u32 phy_configuration; u32 parity_check = 0; u32 parity_count = 0; u32 llctl, link_rate; u32 clksm_value = 0; u32 sp_timeouts = 0; - iphy->link_layer_registers = reg; + phy_user = &ihost->user_parameters.phys[phy_idx]; + phy_oem = &ihost->oem_parameters.phys[phy_idx]; + iphy->link_layer_registers = llr; /* Set our IDENTIFY frame data */ #define SCI_END_DEVICE 0x01 @@ -116,32 +117,26 @@ sci_phy_link_layer_initialization(struct isci_phy *iphy, SCU_SAS_TIID_GEN_BIT(STP_INITIATOR) | SCU_SAS_TIID_GEN_BIT(DA_SATA_HOST) | SCU_SAS_TIID_GEN_VAL(DEVICE_TYPE, SCI_END_DEVICE), - &iphy->link_layer_registers->transmit_identification); + &llr->transmit_identification); /* Write the device SAS Address */ - writel(0xFEDCBA98, - &iphy->link_layer_registers->sas_device_name_high); - writel(phy_idx, &iphy->link_layer_registers->sas_device_name_low); + writel(0xFEDCBA98, &llr->sas_device_name_high); + writel(phy_idx, &llr->sas_device_name_low); /* Write the source SAS Address */ - writel(phy_oem->sas_address.high, - &iphy->link_layer_registers->source_sas_address_high); - writel(phy_oem->sas_address.low, - &iphy->link_layer_registers->source_sas_address_low); + writel(phy_oem->sas_address.high, &llr->source_sas_address_high); + writel(phy_oem->sas_address.low, &llr->source_sas_address_low); /* Clear and Set the PHY Identifier */ - writel(0, &iphy->link_layer_registers->identify_frame_phy_id); - writel(SCU_SAS_TIPID_GEN_VALUE(ID, phy_idx), - &iphy->link_layer_registers->identify_frame_phy_id); + writel(0, &llr->identify_frame_phy_id); + writel(SCU_SAS_TIPID_GEN_VALUE(ID, phy_idx), &llr->identify_frame_phy_id); /* Change the initial state of the phy configuration register */ - phy_configuration = - readl(&iphy->link_layer_registers->phy_configuration); + phy_configuration = readl(&llr->phy_configuration); /* Hold OOB state machine in reset */ phy_configuration |= SCU_SAS_PCFG_GEN_BIT(OOB_RESET); - writel(phy_configuration, - &iphy->link_layer_registers->phy_configuration); + writel(phy_configuration, &llr->phy_configuration); /* Configure the SNW capabilities */ phy_cap.all = 0; @@ -149,15 +144,64 @@ sci_phy_link_layer_initialization(struct isci_phy *iphy, phy_cap.gen3_no_ssc = 1; phy_cap.gen2_no_ssc = 1; phy_cap.gen1_no_ssc = 1; - if (ihost->oem_parameters.controller.do_enable_ssc == true) { + if (ihost->oem_parameters.controller.do_enable_ssc) { + struct scu_afe_registers __iomem *afe = &ihost->scu_registers->afe; + struct scu_afe_transceiver *xcvr = &afe->scu_afe_xcvr[phy_idx]; + struct isci_pci_info *pci_info = to_pci_info(ihost->pdev); + bool en_sas = false; + bool en_sata = false; + u32 sas_type = 0; + u32 sata_spread = 0x2; + u32 sas_spread = 0x2; + phy_cap.gen3_ssc = 1; phy_cap.gen2_ssc = 1; phy_cap.gen1_ssc = 1; + + if (pci_info->orom->hdr.version < ISCI_ROM_VER_1_1) + en_sas = en_sata = true; + else { + sata_spread = ihost->oem_parameters.controller.ssc_sata_tx_spread_level; + sas_spread = ihost->oem_parameters.controller.ssc_sas_tx_spread_level; + + if (sata_spread) + en_sata = true; + + if (sas_spread) { + en_sas = true; + sas_type = ihost->oem_parameters.controller.ssc_sas_tx_type; + } + + } + + if (en_sas) { + u32 reg; + + reg = readl(&xcvr->afe_xcvr_control0); + reg |= (0x00100000 | (sas_type << 19)); + writel(reg, &xcvr->afe_xcvr_control0); + + reg = readl(&xcvr->afe_tx_ssc_control); + reg |= sas_spread << 8; + writel(reg, &xcvr->afe_tx_ssc_control); + } + + if (en_sata) { + u32 reg; + + reg = readl(&xcvr->afe_tx_ssc_control); + reg |= sata_spread; + writel(reg, &xcvr->afe_tx_ssc_control); + + reg = readl(&llr->stp_control); + reg |= 1 << 12; + writel(reg, &llr->stp_control); + } } - /* - * The SAS specification indicates that the phy_capabilities that - * are transmitted shall have an even parity. Calculate the parity. */ + /* The SAS specification indicates that the phy_capabilities that + * are transmitted shall have an even parity. Calculate the parity. + */ parity_check = phy_cap.all; while (parity_check != 0) { if (parity_check & 0x1) @@ -165,20 +209,20 @@ sci_phy_link_layer_initialization(struct isci_phy *iphy, parity_check >>= 1; } - /* - * If parity indicates there are an odd number of bits set, then - * set the parity bit to 1 in the phy capabilities. */ + /* If parity indicates there are an odd number of bits set, then + * set the parity bit to 1 in the phy capabilities. + */ if ((parity_count % 2) != 0) phy_cap.parity = 1; - writel(phy_cap.all, &iphy->link_layer_registers->phy_capabilities); + writel(phy_cap.all, &llr->phy_capabilities); /* Set the enable spinup period but disable the ability to send * notify enable spinup */ writel(SCU_ENSPINUP_GEN_VAL(COUNT, phy_user->notify_enable_spin_up_insertion_frequency), - &iphy->link_layer_registers->notify_enable_spinup_control); + &llr->notify_enable_spinup_control); /* Write the ALIGN Insertion Ferequency for connected phy and * inpendent of connected state @@ -189,11 +233,13 @@ sci_phy_link_layer_initialization(struct isci_phy *iphy, clksm_value |= SCU_ALIGN_INSERTION_FREQUENCY_GEN_VAL(GENERAL, phy_user->align_insertion_frequency); - writel(clksm_value, &iphy->link_layer_registers->clock_skew_management); + writel(clksm_value, &llr->clock_skew_management); - /* @todo Provide a way to write this register correctly */ - writel(0x02108421, - &iphy->link_layer_registers->afe_lookup_table_control); + if (is_c0(ihost->pdev) || is_c1(ihost->pdev)) { + writel(0x04210400, &llr->afe_lookup_table_control); + writel(0x020A7C05, &llr->sas_primitive_timeout); + } else + writel(0x02108421, &llr->afe_lookup_table_control); llctl = SCU_SAS_LLCTL_GEN_VAL(NO_OUTBOUND_TASK_TIMEOUT, (u8)ihost->user_parameters.no_outbound_task_timeout); @@ -210,9 +256,9 @@ sci_phy_link_layer_initialization(struct isci_phy *iphy, break; } llctl |= SCU_SAS_LLCTL_GEN_VAL(MAX_LINK_RATE, link_rate); - writel(llctl, &iphy->link_layer_registers->link_layer_control); + writel(llctl, &llr->link_layer_control); - sp_timeouts = readl(&iphy->link_layer_registers->sas_phy_timeouts); + sp_timeouts = readl(&llr->sas_phy_timeouts); /* Clear the default 0x36 (54us) RATE_CHANGE timeout value. */ sp_timeouts &= ~SCU_SAS_PHYTOV_GEN_VAL(RATE_CHANGE, 0xFF); @@ -222,20 +268,23 @@ sci_phy_link_layer_initialization(struct isci_phy *iphy, */ sp_timeouts |= SCU_SAS_PHYTOV_GEN_VAL(RATE_CHANGE, 0x3B); - writel(sp_timeouts, &iphy->link_layer_registers->sas_phy_timeouts); + writel(sp_timeouts, &llr->sas_phy_timeouts); if (is_a2(ihost->pdev)) { - /* Program the max ARB time for the PHY to 700us so we inter-operate with - * the PMC expander which shuts down PHYs if the expander PHY generates too - * many breaks. This time value will guarantee that the initiator PHY will - * generate the break. + /* Program the max ARB time for the PHY to 700us so we + * inter-operate with the PMC expander which shuts down + * PHYs if the expander PHY generates too many breaks. + * This time value will guarantee that the initiator PHY + * will generate the break. */ writel(SCIC_SDS_PHY_MAX_ARBITRATION_WAIT_TIME, - &iphy->link_layer_registers->maximum_arbitration_wait_timer_timeout); + &llr->maximum_arbitration_wait_timer_timeout); } - /* Disable link layer hang detection, rely on the OS timeout for I/O timeouts. */ - writel(0, &iphy->link_layer_registers->link_layer_hang_detection_timeout); + /* Disable link layer hang detection, rely on the OS timeout for + * I/O timeouts. + */ + writel(0, &llr->link_layer_hang_detection_timeout); /* We can exit the initial state to the stopped state */ sci_change_state(&iphy->sm, SCI_PHY_STOPPED); @@ -1049,24 +1098,25 @@ static void scu_link_layer_stop_protocol_engine( writel(enable_spinup_value, &iphy->link_layer_registers->notify_enable_spinup_control); } -/** - * - * - * This method will start the OOB/SN state machine for this struct isci_phy object. - */ -static void scu_link_layer_start_oob( - struct isci_phy *iphy) +static void scu_link_layer_start_oob(struct isci_phy *iphy) { - u32 scu_sas_pcfg_value; - - scu_sas_pcfg_value = - readl(&iphy->link_layer_registers->phy_configuration); - scu_sas_pcfg_value |= SCU_SAS_PCFG_GEN_BIT(OOB_ENABLE); - scu_sas_pcfg_value &= - ~(SCU_SAS_PCFG_GEN_BIT(OOB_RESET) | - SCU_SAS_PCFG_GEN_BIT(HARD_RESET)); - writel(scu_sas_pcfg_value, - &iphy->link_layer_registers->phy_configuration); + struct scu_link_layer_registers __iomem *ll = iphy->link_layer_registers; + u32 val; + + /** Reset OOB sequence - start */ + val = readl(&ll->phy_configuration); + val &= ~(SCU_SAS_PCFG_GEN_BIT(OOB_RESET) | + SCU_SAS_PCFG_GEN_BIT(HARD_RESET)); + writel(val, &ll->phy_configuration); + readl(&ll->phy_configuration); /* flush */ + /** Reset OOB sequence - end */ + + /** Start OOB sequence - start */ + val = readl(&ll->phy_configuration); + val |= SCU_SAS_PCFG_GEN_BIT(OOB_ENABLE); + writel(val, &ll->phy_configuration); + readl(&ll->phy_configuration); /* flush */ + /** Start OOB sequence - end */ } /** |