diff options
author | Benjamin Rood <benjaminjrood@gmail.com> | 2015-10-30 10:53:28 -0400 |
---|---|---|
committer | Martin K. Petersen <martin.petersen@oracle.com> | 2015-11-02 23:39:02 -0500 |
commit | c5614df7ffa74d2fcb591eb4e9008ca38f0bc8c1 (patch) | |
tree | 50a3b2c6f0dcdd7920c0fc8e945d267d4363a5a9 /drivers/scsi/pm8001 | |
parent | 10efa460fe23c4def83fb98be311502b5c5961fa (diff) |
pm80xx: set PHY profiles for ATTO 12Gb SAS controllers
PHY profiles are not saved in NVRAM on ATTO 12Gb SAS controllers.
Therefore, in order for the controller to function in a wide range of
configurations, the PHY profiles must be statically set. This patch
provides the necessary functionality to do so.
Signed-off-by: Benjamin Rood <brood@attotech.com>
Reviewed-by: Jack Wang <jinpu.wang@profitbricks.com>
Reviewed-by: Hannes Reinecke <hare@suse.de>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
Diffstat (limited to 'drivers/scsi/pm8001')
-rw-r--r-- | drivers/scsi/pm8001/pm8001_init.c | 130 | ||||
-rw-r--r-- | drivers/scsi/pm8001/pm8001_sas.h | 2 | ||||
-rw-r--r-- | drivers/scsi/pm8001/pm80xx_hwi.c | 32 |
3 files changed, 164 insertions, 0 deletions
diff --git a/drivers/scsi/pm8001/pm8001_init.c b/drivers/scsi/pm8001/pm8001_init.c index 861416f12655..7ce7ea3d0161 100644 --- a/drivers/scsi/pm8001/pm8001_init.c +++ b/drivers/scsi/pm8001/pm8001_init.c | |||
@@ -732,6 +732,131 @@ static int pm8001_get_phy_settings_info(struct pm8001_hba_info *pm8001_ha) | |||
732 | return 0; | 732 | return 0; |
733 | } | 733 | } |
734 | 734 | ||
735 | struct pm8001_mpi3_phy_pg_trx_config { | ||
736 | u32 LaneLosCfg; | ||
737 | u32 LanePgaCfg1; | ||
738 | u32 LanePisoCfg1; | ||
739 | u32 LanePisoCfg2; | ||
740 | u32 LanePisoCfg3; | ||
741 | u32 LanePisoCfg4; | ||
742 | u32 LanePisoCfg5; | ||
743 | u32 LanePisoCfg6; | ||
744 | u32 LaneBctCtrl; | ||
745 | }; | ||
746 | |||
747 | /** | ||
748 | * pm8001_get_internal_phy_settings : Retrieves the internal PHY settings | ||
749 | * @pm8001_ha : our adapter | ||
750 | * @phycfg : PHY config page to populate | ||
751 | */ | ||
752 | static | ||
753 | void pm8001_get_internal_phy_settings(struct pm8001_hba_info *pm8001_ha, | ||
754 | struct pm8001_mpi3_phy_pg_trx_config *phycfg) | ||
755 | { | ||
756 | phycfg->LaneLosCfg = 0x00000132; | ||
757 | phycfg->LanePgaCfg1 = 0x00203949; | ||
758 | phycfg->LanePisoCfg1 = 0x000000FF; | ||
759 | phycfg->LanePisoCfg2 = 0xFF000001; | ||
760 | phycfg->LanePisoCfg3 = 0xE7011300; | ||
761 | phycfg->LanePisoCfg4 = 0x631C40C0; | ||
762 | phycfg->LanePisoCfg5 = 0xF8102036; | ||
763 | phycfg->LanePisoCfg6 = 0xF74A1000; | ||
764 | phycfg->LaneBctCtrl = 0x00FB33F8; | ||
765 | } | ||
766 | |||
767 | /** | ||
768 | * pm8001_get_external_phy_settings : Retrieves the external PHY settings | ||
769 | * @pm8001_ha : our adapter | ||
770 | * @phycfg : PHY config page to populate | ||
771 | */ | ||
772 | static | ||
773 | void pm8001_get_external_phy_settings(struct pm8001_hba_info *pm8001_ha, | ||
774 | struct pm8001_mpi3_phy_pg_trx_config *phycfg) | ||
775 | { | ||
776 | phycfg->LaneLosCfg = 0x00000132; | ||
777 | phycfg->LanePgaCfg1 = 0x00203949; | ||
778 | phycfg->LanePisoCfg1 = 0x000000FF; | ||
779 | phycfg->LanePisoCfg2 = 0xFF000001; | ||
780 | phycfg->LanePisoCfg3 = 0xE7011300; | ||
781 | phycfg->LanePisoCfg4 = 0x63349140; | ||
782 | phycfg->LanePisoCfg5 = 0xF8102036; | ||
783 | phycfg->LanePisoCfg6 = 0xF80D9300; | ||
784 | phycfg->LaneBctCtrl = 0x00FB33F8; | ||
785 | } | ||
786 | |||
787 | /** | ||
788 | * pm8001_get_phy_mask : Retrieves the mask that denotes if a PHY is int/ext | ||
789 | * @pm8001_ha : our adapter | ||
790 | * @phymask : The PHY mask | ||
791 | */ | ||
792 | static | ||
793 | void pm8001_get_phy_mask(struct pm8001_hba_info *pm8001_ha, int *phymask) | ||
794 | { | ||
795 | switch (pm8001_ha->pdev->subsystem_device) { | ||
796 | case 0x0070: /* H1280 - 8 external 0 internal */ | ||
797 | case 0x0072: /* H12F0 - 16 external 0 internal */ | ||
798 | *phymask = 0x0000; | ||
799 | break; | ||
800 | |||
801 | case 0x0071: /* H1208 - 0 external 8 internal */ | ||
802 | case 0x0073: /* H120F - 0 external 16 internal */ | ||
803 | *phymask = 0xFFFF; | ||
804 | break; | ||
805 | |||
806 | case 0x0080: /* H1244 - 4 external 4 internal */ | ||
807 | *phymask = 0x00F0; | ||
808 | break; | ||
809 | |||
810 | case 0x0081: /* H1248 - 4 external 8 internal */ | ||
811 | *phymask = 0x0FF0; | ||
812 | break; | ||
813 | |||
814 | case 0x0082: /* H1288 - 8 external 8 internal */ | ||
815 | *phymask = 0xFF00; | ||
816 | break; | ||
817 | |||
818 | default: | ||
819 | PM8001_INIT_DBG(pm8001_ha, | ||
820 | pm8001_printk("Unknown subsystem device=0x%.04x", | ||
821 | pm8001_ha->pdev->subsystem_device)); | ||
822 | } | ||
823 | } | ||
824 | |||
825 | /** | ||
826 | * pm8001_set_phy_settings_ven_117c_12Gb : Configure ATTO 12Gb PHY settings | ||
827 | * @pm8001_ha : our adapter | ||
828 | */ | ||
829 | static | ||
830 | int pm8001_set_phy_settings_ven_117c_12G(struct pm8001_hba_info *pm8001_ha) | ||
831 | { | ||
832 | struct pm8001_mpi3_phy_pg_trx_config phycfg_int; | ||
833 | struct pm8001_mpi3_phy_pg_trx_config phycfg_ext; | ||
834 | int phymask = 0; | ||
835 | int i = 0; | ||
836 | |||
837 | memset(&phycfg_int, 0, sizeof(phycfg_int)); | ||
838 | memset(&phycfg_ext, 0, sizeof(phycfg_ext)); | ||
839 | |||
840 | pm8001_get_internal_phy_settings(pm8001_ha, &phycfg_int); | ||
841 | pm8001_get_external_phy_settings(pm8001_ha, &phycfg_ext); | ||
842 | pm8001_get_phy_mask(pm8001_ha, &phymask); | ||
843 | |||
844 | for (i = 0; i < pm8001_ha->chip->n_phy; i++) { | ||
845 | if (phymask & (1 << i)) {/* Internal PHY */ | ||
846 | pm8001_set_phy_profile_single(pm8001_ha, i, | ||
847 | sizeof(phycfg_int) / sizeof(u32), | ||
848 | (u32 *)&phycfg_int); | ||
849 | |||
850 | } else { /* External PHY */ | ||
851 | pm8001_set_phy_profile_single(pm8001_ha, i, | ||
852 | sizeof(phycfg_ext) / sizeof(u32), | ||
853 | (u32 *)&phycfg_ext); | ||
854 | } | ||
855 | } | ||
856 | |||
857 | return 0; | ||
858 | } | ||
859 | |||
735 | /** | 860 | /** |
736 | * pm8001_configure_phy_settings : Configures PHY settings based on vendor ID. | 861 | * pm8001_configure_phy_settings : Configures PHY settings based on vendor ID. |
737 | * @pm8001_ha : our hba. | 862 | * @pm8001_ha : our hba. |
@@ -740,6 +865,11 @@ static int pm8001_configure_phy_settings(struct pm8001_hba_info *pm8001_ha) | |||
740 | { | 865 | { |
741 | switch (pm8001_ha->pdev->subsystem_vendor) { | 866 | switch (pm8001_ha->pdev->subsystem_vendor) { |
742 | case PCI_VENDOR_ID_ATTO: | 867 | case PCI_VENDOR_ID_ATTO: |
868 | if (pm8001_ha->pdev->device == 0x0042) /* 6Gb */ | ||
869 | return 0; | ||
870 | else | ||
871 | return pm8001_set_phy_settings_ven_117c_12G(pm8001_ha); | ||
872 | |||
743 | case PCI_VENDOR_ID_ADAPTEC2: | 873 | case PCI_VENDOR_ID_ADAPTEC2: |
744 | case 0: | 874 | case 0: |
745 | return 0; | 875 | return 0; |
diff --git a/drivers/scsi/pm8001/pm8001_sas.h b/drivers/scsi/pm8001/pm8001_sas.h index 9fa9705653ae..6628cc38316c 100644 --- a/drivers/scsi/pm8001/pm8001_sas.h +++ b/drivers/scsi/pm8001/pm8001_sas.h | |||
@@ -710,6 +710,8 @@ int pm80xx_set_thermal_config(struct pm8001_hba_info *pm8001_ha); | |||
710 | int pm8001_bar4_shift(struct pm8001_hba_info *pm8001_ha, u32 shiftValue); | 710 | int pm8001_bar4_shift(struct pm8001_hba_info *pm8001_ha, u32 shiftValue); |
711 | void pm8001_set_phy_profile(struct pm8001_hba_info *pm8001_ha, | 711 | void pm8001_set_phy_profile(struct pm8001_hba_info *pm8001_ha, |
712 | u32 length, u8 *buf); | 712 | u32 length, u8 *buf); |
713 | void pm8001_set_phy_profile_single(struct pm8001_hba_info *pm8001_ha, | ||
714 | u32 phy, u32 length, u32 *buf); | ||
713 | int pm80xx_bar4_shift(struct pm8001_hba_info *pm8001_ha, u32 shiftValue); | 715 | int pm80xx_bar4_shift(struct pm8001_hba_info *pm8001_ha, u32 shiftValue); |
714 | ssize_t pm80xx_get_fatal_dump(struct device *cdev, | 716 | ssize_t pm80xx_get_fatal_dump(struct device *cdev, |
715 | struct device_attribute *attr, char *buf); | 717 | struct device_attribute *attr, char *buf); |
diff --git a/drivers/scsi/pm8001/pm80xx_hwi.c b/drivers/scsi/pm8001/pm80xx_hwi.c index 9a389f1508de..29c548be6438 100644 --- a/drivers/scsi/pm8001/pm80xx_hwi.c +++ b/drivers/scsi/pm8001/pm80xx_hwi.c | |||
@@ -4576,6 +4576,38 @@ void pm8001_set_phy_profile(struct pm8001_hba_info *pm8001_ha, | |||
4576 | } | 4576 | } |
4577 | PM8001_INIT_DBG(pm8001_ha, pm8001_printk("phy settings completed\n")); | 4577 | PM8001_INIT_DBG(pm8001_ha, pm8001_printk("phy settings completed\n")); |
4578 | } | 4578 | } |
4579 | |||
4580 | void pm8001_set_phy_profile_single(struct pm8001_hba_info *pm8001_ha, | ||
4581 | u32 phy, u32 length, u32 *buf) | ||
4582 | { | ||
4583 | u32 tag, opc; | ||
4584 | int rc, i; | ||
4585 | struct set_phy_profile_req payload; | ||
4586 | struct inbound_queue_table *circularQ; | ||
4587 | |||
4588 | memset(&payload, 0, sizeof(payload)); | ||
4589 | |||
4590 | rc = pm8001_tag_alloc(pm8001_ha, &tag); | ||
4591 | if (rc) | ||
4592 | PM8001_INIT_DBG(pm8001_ha, pm8001_printk("Invalid tag")); | ||
4593 | |||
4594 | circularQ = &pm8001_ha->inbnd_q_tbl[0]; | ||
4595 | opc = OPC_INB_SET_PHY_PROFILE; | ||
4596 | |||
4597 | payload.tag = cpu_to_le32(tag); | ||
4598 | payload.ppc_phyid = (((SAS_PHY_ANALOG_SETTINGS_PAGE & 0xF) << 8) | ||
4599 | | (phy & 0xFF)); | ||
4600 | |||
4601 | for (i = 0; i < length; i++) | ||
4602 | payload.reserved[i] = cpu_to_le32(*(buf + i)); | ||
4603 | |||
4604 | rc = pm8001_mpi_build_cmd(pm8001_ha, circularQ, opc, &payload, 0); | ||
4605 | if (rc) | ||
4606 | pm8001_tag_free(pm8001_ha, tag); | ||
4607 | |||
4608 | PM8001_INIT_DBG(pm8001_ha, | ||
4609 | pm8001_printk("PHY %d settings applied", phy)); | ||
4610 | } | ||
4579 | const struct pm8001_dispatch pm8001_80xx_dispatch = { | 4611 | const struct pm8001_dispatch pm8001_80xx_dispatch = { |
4580 | .name = "pmc80xx", | 4612 | .name = "pmc80xx", |
4581 | .chip_init = pm80xx_chip_init, | 4613 | .chip_init = pm80xx_chip_init, |