Skip to content

Commit

Permalink
[SCSI] mvsas: add support for 94xx phy tuning and multiple revisions
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Xiangliang Yu authored and James Bottomley committed Jul 26, 2011
1 parent 534ff10 commit f1f82a9
Show file tree
Hide file tree
Showing 4 changed files with 382 additions and 14 deletions.
267 changes: 253 additions & 14 deletions drivers/scsi/mvsas/mv_94xx.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -90,20 +300,35 @@ 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)
{
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);
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
58 changes: 58 additions & 0 deletions drivers/scsi/mvsas/mv_94xx.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,14 @@

#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 */
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand All @@ -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
Expand Down
Loading

0 comments on commit f1f82a9

Please sign in to comment.