[SCSI] mvsas: add support for 94xx phy tuning and multiple revisions
authorXiangliang Yu <yuxiangl@marvell.com>
Tue, 24 May 2011 14:28:31 +0000 (22:28 +0800)
committerJames Bottomley <JBottomley@Parallels.com>
Tue, 26 Jul 2011 06:30:06 +0000 (10:30 +0400)
Add 94xx phy tuning to aid manufacturing.
Add support for 94xx multiple revisions: A0, B0, C0, C1, C2.

Signed-off-by: Xiangliang Yu <yuxiangl@marvell.com>
Signed-off-by: James Bottomley <JBottomley@Parallels.com>
drivers/scsi/mvsas/mv_94xx.c
drivers/scsi/mvsas/mv_94xx.h
drivers/scsi/mvsas/mv_init.c
drivers/scsi/mvsas/mv_sas.h

index 9d60c7c19b32977c3968c539b2ec5f0bf5d72087..3f2ad934c91757f6f7a3380308241cf0ef42f719 100644 (file)
@@ -48,6 +48,216 @@ static void mvs_94xx_detect_porttype(struct mvs_info *mvi, int i)
        }
 }
 
+void set_phy_tuning(struct mvs_info *mvi, int phy_id,
+                       struct phy_tuning phy_tuning)
+{
+       u32 tmp, setting_0 = 0, setting_1 = 0;
+       u8 i;
+
+       /* Remap information for B0 chip:
+       *
+       * R0Ch -> R118h[15:0] (Adapted DFE F3 - F5 coefficient)
+       * R0Dh -> R118h[31:16] (Generation 1 Setting 0)
+       * R0Eh -> R11Ch[15:0]  (Generation 1 Setting 1)
+       * R0Fh -> R11Ch[31:16] (Generation 2 Setting 0)
+       * R10h -> R120h[15:0]  (Generation 2 Setting 1)
+       * R11h -> R120h[31:16] (Generation 3 Setting 0)
+       * R12h -> R124h[15:0]  (Generation 3 Setting 1)
+       * R13h -> R124h[31:16] (Generation 4 Setting 0 (Reserved))
+       */
+
+       /* A0 has a different set of registers */
+       if (mvi->pdev->revision == VANIR_A0_REV)
+               return;
+
+       for (i = 0; i < 3; i++) {
+               /* loop 3 times, set Gen 1, Gen 2, Gen 3 */
+               switch (i) {
+               case 0:
+                       setting_0 = GENERATION_1_SETTING;
+                       setting_1 = GENERATION_1_2_SETTING;
+                       break;
+               case 1:
+                       setting_0 = GENERATION_1_2_SETTING;
+                       setting_1 = GENERATION_2_3_SETTING;
+                       break;
+               case 2:
+                       setting_0 = GENERATION_2_3_SETTING;
+                       setting_1 = GENERATION_3_4_SETTING;
+                       break;
+               }
+
+               /* Set:
+               *
+               * Transmitter Emphasis Enable
+               * Transmitter Emphasis Amplitude
+               * Transmitter Amplitude
+               */
+               mvs_write_port_vsr_addr(mvi, phy_id, setting_0);
+               tmp = mvs_read_port_vsr_data(mvi, phy_id);
+               tmp &= ~(0xFBE << 16);
+               tmp |= (((phy_tuning.trans_emp_en << 11) |
+                       (phy_tuning.trans_emp_amp << 7) |
+                       (phy_tuning.trans_amp << 1)) << 16);
+               mvs_write_port_vsr_data(mvi, phy_id, tmp);
+
+               /* Set Transmitter Amplitude Adjust */
+               mvs_write_port_vsr_addr(mvi, phy_id, setting_1);
+               tmp = mvs_read_port_vsr_data(mvi, phy_id);
+               tmp &= ~(0xC000);
+               tmp |= (phy_tuning.trans_amp_adj << 14);
+               mvs_write_port_vsr_data(mvi, phy_id, tmp);
+       }
+}
+
+void set_phy_ffe_tuning(struct mvs_info *mvi, int phy_id,
+                               struct ffe_control ffe)
+{
+       u32 tmp;
+
+       /* Don't run this if A0/B0 */
+       if ((mvi->pdev->revision == VANIR_A0_REV)
+               || (mvi->pdev->revision == VANIR_B0_REV))
+               return;
+
+       /* FFE Resistor and Capacitor */
+       /* R10Ch DFE Resolution Control/Squelch and FFE Setting
+        *
+        * FFE_FORCE            [7]
+        * FFE_RES_SEL          [6:4]
+        * FFE_CAP_SEL          [3:0]
+        */
+       mvs_write_port_vsr_addr(mvi, phy_id, VSR_PHY_FFE_CONTROL);
+       tmp = mvs_read_port_vsr_data(mvi, phy_id);
+       tmp &= ~0xFF;
+
+       /* Read from HBA_Info_Page */
+       tmp |= ((0x1 << 7) |
+               (ffe.ffe_rss_sel << 4) |
+               (ffe.ffe_cap_sel << 0));
+
+       mvs_write_port_vsr_data(mvi, phy_id, tmp);
+
+       /* R064h PHY Mode Register 1
+        *
+        * DFE_DIS              18
+        */
+       mvs_write_port_vsr_addr(mvi, phy_id, VSR_REF_CLOCK_CRTL);
+       tmp = mvs_read_port_vsr_data(mvi, phy_id);
+       tmp &= ~0x40001;
+       /* Hard coding */
+       /* No defines in HBA_Info_Page */
+       tmp |= (0 << 18);
+       mvs_write_port_vsr_data(mvi, phy_id, tmp);
+
+       /* R110h DFE F0-F1 Coefficient Control/DFE Update Control
+        *
+        * DFE_UPDATE_EN        [11:6]
+        * DFE_FX_FORCE         [5:0]
+        */
+       mvs_write_port_vsr_addr(mvi, phy_id, VSR_PHY_DFE_UPDATE_CRTL);
+       tmp = mvs_read_port_vsr_data(mvi, phy_id);
+       tmp &= ~0xFFF;
+       /* Hard coding */
+       /* No defines in HBA_Info_Page */
+       tmp |= ((0x3F << 6) | (0x0 << 0));
+       mvs_write_port_vsr_data(mvi, phy_id, tmp);
+
+       /* R1A0h Interface and Digital Reference Clock Control/Reserved_50h
+        *
+        * FFE_TRAIN_EN         3
+        */
+       mvs_write_port_vsr_addr(mvi, phy_id, VSR_REF_CLOCK_CRTL);
+       tmp = mvs_read_port_vsr_data(mvi, phy_id);
+       tmp &= ~0x8;
+       /* Hard coding */
+       /* No defines in HBA_Info_Page */
+       tmp |= (0 << 3);
+       mvs_write_port_vsr_data(mvi, phy_id, tmp);
+}
+
+/*Notice: this function must be called when phy is disabled*/
+void set_phy_rate(struct mvs_info *mvi, int phy_id, u8 rate)
+{
+       union reg_phy_cfg phy_cfg, phy_cfg_tmp;
+       mvs_write_port_vsr_addr(mvi, phy_id, VSR_PHY_MODE2);
+       phy_cfg_tmp.v = mvs_read_port_vsr_data(mvi, phy_id);
+       phy_cfg.v = 0;
+       phy_cfg.u.disable_phy = phy_cfg_tmp.u.disable_phy;
+       phy_cfg.u.sas_support = 1;
+       phy_cfg.u.sata_support = 1;
+       phy_cfg.u.sata_host_mode = 1;
+
+       switch (rate) {
+       case 0x0:
+               /* support 1.5 Gbps */
+               phy_cfg.u.speed_support = 1;
+               phy_cfg.u.snw_3_support = 0;
+               phy_cfg.u.tx_lnk_parity = 1;
+               phy_cfg.u.tx_spt_phs_lnk_rate = 0x30;
+               break;
+       case 0x1:
+
+               /* support 1.5, 3.0 Gbps */
+               phy_cfg.u.speed_support = 3;
+               phy_cfg.u.tx_spt_phs_lnk_rate = 0x3c;
+               phy_cfg.u.tx_lgcl_lnk_rate = 0x08;
+               break;
+       case 0x2:
+       default:
+               /* support 1.5, 3.0, 6.0 Gbps */
+               phy_cfg.u.speed_support = 7;
+               phy_cfg.u.snw_3_support = 1;
+               phy_cfg.u.tx_lnk_parity = 1;
+               phy_cfg.u.tx_spt_phs_lnk_rate = 0x3f;
+               phy_cfg.u.tx_lgcl_lnk_rate = 0x09;
+               break;
+       }
+       mvs_write_port_vsr_data(mvi, phy_id, phy_cfg.v);
+}
+
+static void __devinit
+mvs_94xx_config_reg_from_hba(struct mvs_info *mvi, int phy_id)
+{
+       u32 temp;
+       temp = (u32)(*(u32 *)&mvi->hba_info_param.phy_tuning[phy_id]);
+       if (temp == 0xFFFFFFFFL) {
+               mvi->hba_info_param.phy_tuning[phy_id].trans_emp_amp = 0x6;
+               mvi->hba_info_param.phy_tuning[phy_id].trans_amp = 0x1A;
+               mvi->hba_info_param.phy_tuning[phy_id].trans_amp_adj = 0x3;
+       }
+
+       temp = (u8)(*(u8 *)&mvi->hba_info_param.ffe_ctl[phy_id]);
+       if (temp == 0xFFL) {
+               switch (mvi->pdev->revision) {
+               case VANIR_A0_REV:
+               case VANIR_B0_REV:
+                       mvi->hba_info_param.ffe_ctl[phy_id].ffe_rss_sel = 0x7;
+                       mvi->hba_info_param.ffe_ctl[phy_id].ffe_cap_sel = 0x7;
+                       break;
+               case VANIR_C0_REV:
+               case VANIR_C1_REV:
+               case VANIR_C2_REV:
+               default:
+                       mvi->hba_info_param.ffe_ctl[phy_id].ffe_rss_sel = 0x7;
+                       mvi->hba_info_param.ffe_ctl[phy_id].ffe_cap_sel = 0xC;
+                       break;
+               }
+       }
+
+       temp = (u8)(*(u8 *)&mvi->hba_info_param.phy_rate[phy_id]);
+       if (temp == 0xFFL)
+               /*set default phy_rate = 6Gbps*/
+               mvi->hba_info_param.phy_rate[phy_id] = 0x2;
+
+       set_phy_tuning(mvi, phy_id,
+               mvi->hba_info_param.phy_tuning[phy_id]);
+       set_phy_ffe_tuning(mvi, phy_id,
+               mvi->hba_info_param.ffe_ctl[phy_id]);
+       set_phy_rate(mvi, phy_id,
+               mvi->hba_info_param.phy_rate[phy_id]);
+}
+
 static void __devinit mvs_94xx_enable_xmt(struct mvs_info *mvi, int phy_id)
 {
        void __iomem *regs = mvi->regs;
@@ -90,12 +300,25 @@ static void mvs_94xx_phy_disable(struct mvs_info *mvi, u32 phy_id)
 
 static void mvs_94xx_phy_enable(struct mvs_info *mvi, u32 phy_id)
 {
-       mvs_write_port_vsr_addr(mvi, phy_id, 0x1B4);
-       mvs_write_port_vsr_data(mvi, phy_id, 0x8300ffc1);
-       mvs_write_port_vsr_addr(mvi, phy_id, 0x104);
-       mvs_write_port_vsr_data(mvi, phy_id, 0x00018080);
+       u32 tmp;
+       u8 revision = 0;
+
+       revision = mvi->pdev->revision;
+       if (revision == VANIR_A0_REV) {
+               mvs_write_port_vsr_addr(mvi, phy_id, CMD_HOST_RD_DATA);
+               mvs_write_port_vsr_data(mvi, phy_id, 0x8300ffc1);
+       }
+       if (revision == VANIR_B0_REV) {
+               mvs_write_port_vsr_addr(mvi, phy_id, CMD_APP_MEM_CTL);
+               mvs_write_port_vsr_data(mvi, phy_id, 0x08001006);
+               mvs_write_port_vsr_addr(mvi, phy_id, CMD_HOST_RD_DATA);
+               mvs_write_port_vsr_data(mvi, phy_id, 0x0000705f);
+       }
+
        mvs_write_port_vsr_addr(mvi, phy_id, VSR_PHY_MODE2);
-       mvs_write_port_vsr_data(mvi, phy_id, 0x00207fff);
+       tmp = mvs_read_port_vsr_data(mvi, phy_id);
+       tmp |= bit(0);
+       mvs_write_port_vsr_data(mvi, phy_id, tmp & 0xfd7fffff);
 }
 
 static int __devinit mvs_94xx_init(struct mvs_info *mvi)
@@ -103,7 +326,9 @@ static int __devinit mvs_94xx_init(struct mvs_info *mvi)
        void __iomem *regs = mvi->regs;
        int i;
        u32 tmp, cctl;
+       u8 revision;
 
+       revision = mvi->pdev->revision;
        mvs_show_pcie_usage(mvi);
        if (mvi->flags & MVF_FLAG_SOC) {
                tmp = mr32(MVS_PHY_CTL);
@@ -133,6 +358,28 @@ static int __devinit mvs_94xx_init(struct mvs_info *mvi)
                msleep(100);
        }
 
+       /* disable Multiplexing, enable phy implemented */
+       mw32(MVS_PORTS_IMP, 0xFF);
+
+       if (revision == VANIR_A0_REV) {
+               mw32(MVS_PA_VSR_ADDR, CMD_CMWK_OOB_DET);
+               mw32(MVS_PA_VSR_PORT, 0x00018080);
+       }
+       mw32(MVS_PA_VSR_ADDR, VSR_PHY_MODE2);
+       if (revision == VANIR_A0_REV || revision == VANIR_B0_REV)
+               /* set 6G/3G/1.5G, multiplexing, without SSC */
+               mw32(MVS_PA_VSR_PORT, 0x0084d4fe);
+       else
+               /* set 6G/3G/1.5G, multiplexing, with and without SSC */
+               mw32(MVS_PA_VSR_PORT, 0x0084fffe);
+
+       if (revision == VANIR_B0_REV) {
+               mw32(MVS_PA_VSR_ADDR, CMD_APP_MEM_CTL);
+               mw32(MVS_PA_VSR_PORT, 0x08001006);
+               mw32(MVS_PA_VSR_ADDR, CMD_HOST_RD_DATA);
+               mw32(MVS_PA_VSR_PORT, 0x0000705f);
+       }
+
        /* reset control */
        mw32(MVS_PCS, 0);               /* MVS_PCS */
        mw32(MVS_STP_REG_SET_0, 0);
@@ -141,15 +388,6 @@ static int __devinit mvs_94xx_init(struct mvs_info *mvi)
        /* init phys */
        mvs_phy_hacks(mvi);
 
-       /* disable Multiplexing, enable phy implemented */
-       mw32(MVS_PORTS_IMP, 0xFF);
-
-
-       mw32(MVS_PA_VSR_ADDR, 0x00000104);
-       mw32(MVS_PA_VSR_PORT, 0x00018080);
-       mw32(MVS_PA_VSR_ADDR, VSR_PHY_MODE8);
-       mw32(MVS_PA_VSR_PORT, 0x0084ffff);
-
        /* set LED blink when IO*/
        mw32(MVS_PA_VSR_ADDR, 0x00000030);
        tmp = mr32(MVS_PA_VSR_PORT);
@@ -178,6 +416,7 @@ static int __devinit mvs_94xx_init(struct mvs_info *mvi)
                                                (mvi->phy[i].dev_sas_addr));
 
                mvs_94xx_enable_xmt(mvi, i);
+               mvs_94xx_config_reg_from_hba(mvi, i);
                mvs_94xx_phy_enable(mvi, i);
 
                mvs_94xx_phy_reset(mvi, i, 1);
index 8835befe2c0e2ea33ea849eae9a81b16736a0a0c..33af2a1fb72980cbae6e41ded36cc92602d7decd 100644 (file)
 
 #define MAX_LINK_RATE          SAS_LINK_RATE_6_0_GBPS
 
+enum VANIR_REVISION_ID {
+       VANIR_A0_REV            = 0xA0,
+       VANIR_B0_REV            = 0x01,
+       VANIR_C0_REV            = 0x02,
+       VANIR_C1_REV            = 0x03,
+       VANIR_C2_REV            = 0xC2,
+};
+
 enum hw_registers {
        MVS_GBL_CTL             = 0x04,  /* global control */
        MVS_GBL_INT_STAT        = 0x00,  /* global irq status */
@@ -126,6 +134,10 @@ enum sas_sata_vsp_regs {
        VSR_PHY_MODE11          = 0x0B * 4, /* Phy Mode */
        VSR_PHY_VS0             = 0x0C * 4, /* Vednor Specific 0 */
        VSR_PHY_VS1             = 0x0D * 4, /* Vednor Specific 1 */
+
+       VSR_PHY_FFE_CONTROL     = 0x10C,
+       VSR_PHY_DFE_UPDATE_CRTL = 0x110,
+       VSR_REF_CLOCK_CRTL      = 0x1A0,
 };
 
 enum chip_register_bits {
@@ -169,6 +181,41 @@ enum pci_interrupt_cause {
        IRQ_PCIE_ERR                   = (1 << 31),
 };
 
+union reg_phy_cfg {
+       u32 v;
+       struct {
+               u32 phy_reset:1;
+               u32 sas_support:1;
+               u32 sata_support:1;
+               u32 sata_host_mode:1;
+               /*
+                * bit 2: 6Gbps support
+                * bit 1: 3Gbps support
+                * bit 0: 1.5Gbps support
+                */
+               u32 speed_support:3;
+               u32 snw_3_support:1;
+               u32 tx_lnk_parity:1;
+               /*
+                * bit 5: G1 (1.5Gbps) Without SSC
+                * bit 4: G1 (1.5Gbps) with SSC
+                * bit 3: G2 (3.0Gbps) Without SSC
+                * bit 2: G2 (3.0Gbps) with SSC
+                * bit 1: G3 (6.0Gbps) without SSC
+                * bit 0: G3 (6.0Gbps) with SSC
+                */
+               u32 tx_spt_phs_lnk_rate:6;
+               /* 8h: 1.5Gbps 9h: 3Gbps Ah: 6Gbps */
+               u32 tx_lgcl_lnk_rate:4;
+               u32 tx_ssc_type:1;
+               u32 sata_spin_up_spt:1;
+               u32 sata_spin_up_en:1;
+               u32 bypass_oob:1;
+               u32 disable_phy:1;
+               u32 rsvd:8;
+       } u;
+};
+
 #define MAX_SG_ENTRY           255
 
 struct mvs_prd_imt {
@@ -185,6 +232,17 @@ struct mvs_prd {
        struct mvs_prd_imt      im_len;
 } __attribute__ ((packed));
 
+/*
+ * these registers are accessed through port vendor
+ * specific address/data registers
+ */
+enum sas_sata_phy_regs {
+       GENERATION_1_SETTING            = 0x118,
+       GENERATION_1_2_SETTING          = 0x11C,
+       GENERATION_2_3_SETTING          = 0x120,
+       GENERATION_3_4_SETTING          = 0x124,
+};
+
 #define SPI_CTRL_REG_94XX              0xc800
 #define SPI_ADDR_REG_94XX              0xc804
 #define SPI_WR_DATA_REG_94XX         0xc808
index 90b636611cde1c3a4f9ee4c2ce3aef9475847d57..9f1cccc2a3ed7e98a9b733faa14df94fb55eed31 100644 (file)
@@ -569,6 +569,9 @@ static int __devinit mvs_pci_init(struct pci_dev *pdev,
                        goto err_out_regions;
                }
 
+               memset(&mvi->hba_info_param, 0xFF,
+                       sizeof(struct hba_info_page));
+
                mvs_init_sas_add(mvi);
 
                mvi->instance = nhost;
index f96100d7aee18d70440643c997c0cd672e68c13a..ccd622f7f841f81d0e94cfed6ca291212fb146d8 100644 (file)
@@ -250,6 +250,73 @@ struct mvs_device {
        u16 reserved;
 };
 
+/* Generate  PHY tunning parameters */
+struct phy_tuning {
+       /* 1 bit,  transmitter emphasis enable  */
+       u8      trans_emp_en:1;
+       /* 4 bits, transmitter emphasis amplitude */
+       u8      trans_emp_amp:4;
+       /* 3 bits, reserved space */
+       u8      Reserved_2bit_1:3;
+       /* 5 bits, transmitter amplitude */
+       u8      trans_amp:5;
+       /* 2 bits, transmitter amplitude adjust */
+       u8      trans_amp_adj:2;
+       /* 1 bit, reserved space */
+       u8      resv_2bit_2:1;
+       /* 2 bytes, reserved space */
+       u8      reserved[2];
+};
+
+struct ffe_control {
+       /* 4 bits,  FFE Capacitor Select  (value range 0~F)  */
+       u8 ffe_cap_sel:4;
+       /* 3 bits,  FFE Resistor Select (value range 0~7) */
+       u8 ffe_rss_sel:3;
+       /* 1 bit reserve*/
+       u8 reserved:1;
+};
+
+/*
+ * HBA_Info_Page is saved in Flash/NVRAM, total 256 bytes.
+ * The data area is valid only Signature="MRVL".
+ * If any member fills with 0xFF, the member is invalid.
+ */
+struct hba_info_page {
+       /* Dword 0 */
+       /* 4 bytes, structure signature,should be "MRVL" at first initial */
+       u8 signature[4];
+
+       /* Dword 1-13 */
+       u32 reserved1[13];
+
+       /* Dword 14-29 */
+       /* 64 bytes, SAS address for each port */
+       u64 sas_addr[8];
+
+       /* Dword 30-31 */
+       /* 8 bytes for vanir 8 port PHY FFE seeting
+        * BIT 0~3 : FFE Capacitor select(value range 0~F)
+        * BIT 4~6 : FFE Resistor select(value range 0~7)
+        * BIT 7: reserve.
+        */
+
+       struct ffe_control  ffe_ctl[8];
+       /* Dword 32 -43 */
+       u32 reserved2[12];
+
+       /* Dword 44-45 */
+       /* 8 bytes,  0:  1.5G, 1: 3.0G, should be 0x01 at first initial */
+       u8 phy_rate[8];
+
+       /* Dword 46-53 */
+       /* 32 bytes, PHY tuning parameters for each PHY*/
+       struct phy_tuning   phy_tuning[8];
+
+       /* Dword 54-63 */
+       u32 reserved3[10];
+};     /* total 256 bytes */
+
 struct mvs_slot_info {
        struct list_head entry;
        union {
@@ -338,6 +405,7 @@ struct mvs_info {
        u32 flashsectSize;
 
        void *addon;
+       struct hba_info_page hba_info_param;
        struct mvs_device       devices[MVS_MAX_DEVICES];
 #ifndef DISABLE_HOTPLUG_DMA_FIX
        void *bulk_buffer;