Commit 84fbd0ce authored by Xiangliang Yu's avatar Xiangliang Yu Committed by James Bottomley

[SCSI] mvsas: misc improvements

Change code to match HBA datasheet.
Change code to make it readable.
Add support big endian for mvs_prd_imt.
Add cpu_to_le32 and cpu_to_le64 to use on addr.
Add scan_finished for structure mvs_prv_info.
Signed-off-by: default avatarXiangliang Yu <yuxiangl@marvell.com>
Signed-off-by: default avatarJames Bottomley <JBottomley@Parallels.com>
parent a4632aae
...@@ -58,24 +58,17 @@ static void __devinit mvs_64xx_enable_xmt(struct mvs_info *mvi, int phy_id) ...@@ -58,24 +58,17 @@ static void __devinit mvs_64xx_enable_xmt(struct mvs_info *mvi, int phy_id)
static void __devinit mvs_64xx_phy_hacks(struct mvs_info *mvi) static void __devinit mvs_64xx_phy_hacks(struct mvs_info *mvi)
{ {
void __iomem *regs = mvi->regs; void __iomem *regs = mvi->regs;
int i;
mvs_phy_hacks(mvi); mvs_phy_hacks(mvi);
if (!(mvi->flags & MVF_FLAG_SOC)) { if (!(mvi->flags & MVF_FLAG_SOC)) {
/* TEST - for phy decoding error, adjust voltage levels */ /* TEST - for phy decoding error, adjust voltage levels */
mw32(MVS_P0_VSR_ADDR + 0, 0x8); for (i = 0; i < MVS_SOC_PORTS; i++) {
mw32(MVS_P0_VSR_DATA + 0, 0x2F0); mvs_write_port_vsr_addr(mvi, i, VSR_PHY_MODE8);
mvs_write_port_vsr_data(mvi, i, 0x2F0);
mw32(MVS_P0_VSR_ADDR + 8, 0x8); }
mw32(MVS_P0_VSR_DATA + 8, 0x2F0);
mw32(MVS_P0_VSR_ADDR + 16, 0x8);
mw32(MVS_P0_VSR_DATA + 16, 0x2F0);
mw32(MVS_P0_VSR_ADDR + 24, 0x8);
mw32(MVS_P0_VSR_DATA + 24, 0x2F0);
} else { } else {
int i;
/* disable auto port detection */ /* disable auto port detection */
mw32(MVS_GBL_PORT_TYPE, 0); mw32(MVS_GBL_PORT_TYPE, 0);
for (i = 0; i < mvi->chip->n_phy; i++) { for (i = 0; i < mvi->chip->n_phy; i++) {
...@@ -321,6 +314,11 @@ static int __devinit mvs_64xx_init(struct mvs_info *mvi) ...@@ -321,6 +314,11 @@ static int __devinit mvs_64xx_init(struct mvs_info *mvi)
/* init phys */ /* init phys */
mvs_64xx_phy_hacks(mvi); mvs_64xx_phy_hacks(mvi);
tmp = mvs_cr32(mvi, CMD_PHY_MODE_21);
tmp &= 0x0000ffff;
tmp |= 0x00fa0000;
mvs_cw32(mvi, CMD_PHY_MODE_21, tmp);
/* enable auto port detection */ /* enable auto port detection */
mw32(MVS_GBL_PORT_TYPE, MODE_AUTO_DET_EN); mw32(MVS_GBL_PORT_TYPE, MODE_AUTO_DET_EN);
...@@ -394,13 +392,17 @@ static int __devinit mvs_64xx_init(struct mvs_info *mvi) ...@@ -394,13 +392,17 @@ static int __devinit mvs_64xx_init(struct mvs_info *mvi)
/* reset CMD queue */ /* reset CMD queue */
tmp = mr32(MVS_PCS); tmp = mr32(MVS_PCS);
tmp |= PCS_CMD_RST; tmp |= PCS_CMD_RST;
tmp &= ~PCS_SELF_CLEAR;
mw32(MVS_PCS, tmp); mw32(MVS_PCS, tmp);
/* interrupt coalescing may cause missing HW interrput in some case, /* interrupt coalescing may cause missing HW interrput in some case,
* and the max count is 0x1ff, while our max slot is 0x200, * and the max count is 0x1ff, while our max slot is 0x200,
* it will make count 0. * it will make count 0.
*/ */
tmp = 0; tmp = 0;
mw32(MVS_INT_COAL, tmp); if (MVS_CHIP_SLOT_SZ > 0x1ff)
mw32(MVS_INT_COAL, 0x1ff | COAL_EN);
else
mw32(MVS_INT_COAL, MVS_CHIP_SLOT_SZ | COAL_EN);
tmp = 0x10000 | interrupt_coalescing; tmp = 0x10000 | interrupt_coalescing;
mw32(MVS_INT_COAL_TMOUT, tmp); mw32(MVS_INT_COAL_TMOUT, tmp);
......
...@@ -271,7 +271,14 @@ static void __devinit mvs_94xx_enable_xmt(struct mvs_info *mvi, int phy_id) ...@@ -271,7 +271,14 @@ static void __devinit mvs_94xx_enable_xmt(struct mvs_info *mvi, int phy_id)
static void mvs_94xx_phy_reset(struct mvs_info *mvi, u32 phy_id, int hard) static void mvs_94xx_phy_reset(struct mvs_info *mvi, u32 phy_id, int hard)
{ {
u32 tmp; u32 tmp;
u32 delay = 5000;
if (hard == MVS_PHY_TUNE) {
mvs_write_port_cfg_addr(mvi, phy_id, PHYR_SATA_CTL);
tmp = mvs_read_port_cfg_data(mvi, phy_id);
mvs_write_port_cfg_data(mvi, phy_id, tmp|0x20000000);
mvs_write_port_cfg_data(mvi, phy_id, tmp|0x100000);
return;
}
tmp = mvs_read_port_irq_stat(mvi, phy_id); tmp = mvs_read_port_irq_stat(mvi, phy_id);
tmp &= ~PHYEV_RDY_CH; tmp &= ~PHYEV_RDY_CH;
mvs_write_port_irq_stat(mvi, phy_id, tmp); mvs_write_port_irq_stat(mvi, phy_id, tmp);
...@@ -281,12 +288,15 @@ static void mvs_94xx_phy_reset(struct mvs_info *mvi, u32 phy_id, int hard) ...@@ -281,12 +288,15 @@ static void mvs_94xx_phy_reset(struct mvs_info *mvi, u32 phy_id, int hard)
mvs_write_phy_ctl(mvi, phy_id, tmp); mvs_write_phy_ctl(mvi, phy_id, tmp);
do { do {
tmp = mvs_read_phy_ctl(mvi, phy_id); tmp = mvs_read_phy_ctl(mvi, phy_id);
} while (tmp & PHY_RST_HARD); udelay(10);
delay--;
} while ((tmp & PHY_RST_HARD) && delay);
if (!delay)
mv_dprintk("phy hard reset failed.\n");
} else { } else {
mvs_write_port_vsr_addr(mvi, phy_id, VSR_PHY_STAT); tmp = mvs_read_phy_ctl(mvi, phy_id);
tmp = mvs_read_port_vsr_data(mvi, phy_id);
tmp |= PHY_RST; tmp |= PHY_RST;
mvs_write_port_vsr_data(mvi, phy_id, tmp); mvs_write_phy_ctl(mvi, phy_id, tmp);
} }
} }
...@@ -413,7 +423,7 @@ static int __devinit mvs_94xx_init(struct mvs_info *mvi) ...@@ -413,7 +423,7 @@ static int __devinit mvs_94xx_init(struct mvs_info *mvi)
mvs_94xx_phy_disable(mvi, i); mvs_94xx_phy_disable(mvi, i);
/* set phy local SAS address */ /* set phy local SAS address */
mvs_set_sas_addr(mvi, i, CONFIG_ID_FRAME3, CONFIG_ID_FRAME4, mvs_set_sas_addr(mvi, i, CONFIG_ID_FRAME3, CONFIG_ID_FRAME4,
(mvi->phy[i].dev_sas_addr)); cpu_to_le64(mvi->phy[i].dev_sas_addr));
mvs_94xx_enable_xmt(mvi, i); mvs_94xx_enable_xmt(mvi, i);
mvs_94xx_config_reg_from_hba(mvi, i); mvs_94xx_config_reg_from_hba(mvi, i);
...@@ -459,7 +469,6 @@ static int __devinit mvs_94xx_init(struct mvs_info *mvi) ...@@ -459,7 +469,6 @@ static int __devinit mvs_94xx_init(struct mvs_info *mvi)
*/ */
cctl = mr32(MVS_CTL); cctl = mr32(MVS_CTL);
cctl |= CCTL_ENDIAN_CMD; cctl |= CCTL_ENDIAN_CMD;
cctl |= CCTL_ENDIAN_DATA;
cctl &= ~CCTL_ENDIAN_OPEN; cctl &= ~CCTL_ENDIAN_OPEN;
cctl |= CCTL_ENDIAN_RSP; cctl |= CCTL_ENDIAN_RSP;
mw32_f(MVS_CTL, cctl); mw32_f(MVS_CTL, cctl);
...@@ -467,13 +476,17 @@ static int __devinit mvs_94xx_init(struct mvs_info *mvi) ...@@ -467,13 +476,17 @@ static int __devinit mvs_94xx_init(struct mvs_info *mvi)
/* reset CMD queue */ /* reset CMD queue */
tmp = mr32(MVS_PCS); tmp = mr32(MVS_PCS);
tmp |= PCS_CMD_RST; tmp |= PCS_CMD_RST;
tmp &= ~PCS_SELF_CLEAR;
mw32(MVS_PCS, tmp); mw32(MVS_PCS, tmp);
/* interrupt coalescing may cause missing HW interrput in some case, /* interrupt coalescing may cause missing HW interrput in some case,
* and the max count is 0x1ff, while our max slot is 0x200, * and the max count is 0x1ff, while our max slot is 0x200,
* it will make count 0. * it will make count 0.
*/ */
tmp = 0; tmp = 0;
mw32(MVS_INT_COAL, tmp); if (MVS_CHIP_SLOT_SZ > 0x1ff)
mw32(MVS_INT_COAL, 0x1ff | COAL_EN);
else
mw32(MVS_INT_COAL, MVS_CHIP_SLOT_SZ | COAL_EN);
tmp = 0x10000 | interrupt_coalescing; tmp = 0x10000 | interrupt_coalescing;
mw32(MVS_INT_COAL_TMOUT, tmp); mw32(MVS_INT_COAL_TMOUT, tmp);
...@@ -674,24 +687,16 @@ static void mvs_94xx_non_spec_ncq_error(struct mvs_info *mvi) ...@@ -674,24 +687,16 @@ static void mvs_94xx_non_spec_ncq_error(struct mvs_info *mvi)
static void mvs_94xx_free_reg_set(struct mvs_info *mvi, u8 *tfs) static void mvs_94xx_free_reg_set(struct mvs_info *mvi, u8 *tfs)
{ {
void __iomem *regs = mvi->regs; void __iomem *regs = mvi->regs;
u32 tmp;
u8 reg_set = *tfs; u8 reg_set = *tfs;
if (*tfs == MVS_ID_NOT_MAPPED) if (*tfs == MVS_ID_NOT_MAPPED)
return; return;
mvi->sata_reg_set &= ~bit(reg_set); mvi->sata_reg_set &= ~bit(reg_set);
if (reg_set < 32) { if (reg_set < 32)
w_reg_set_enable(reg_set, (u32)mvi->sata_reg_set); w_reg_set_enable(reg_set, (u32)mvi->sata_reg_set);
tmp = mr32(MVS_INT_STAT_SRS_0) & (u32)mvi->sata_reg_set; else
if (tmp) w_reg_set_enable(reg_set, (u32)(mvi->sata_reg_set >> 32));
mw32(MVS_INT_STAT_SRS_0, tmp);
} else {
w_reg_set_enable(reg_set, mvi->sata_reg_set);
tmp = mr32(MVS_INT_STAT_SRS_1) & mvi->sata_reg_set;
if (tmp)
mw32(MVS_INT_STAT_SRS_1, tmp);
}
*tfs = MVS_ID_NOT_MAPPED; *tfs = MVS_ID_NOT_MAPPED;
...@@ -707,7 +712,7 @@ static u8 mvs_94xx_assign_reg_set(struct mvs_info *mvi, u8 *tfs) ...@@ -707,7 +712,7 @@ static u8 mvs_94xx_assign_reg_set(struct mvs_info *mvi, u8 *tfs)
return 0; return 0;
i = mv_ffc64(mvi->sata_reg_set); i = mv_ffc64(mvi->sata_reg_set);
if (i > 32) { if (i >= 32) {
mvi->sata_reg_set |= bit(i); mvi->sata_reg_set |= bit(i);
w_reg_set_enable(i, (u32)(mvi->sata_reg_set >> 32)); w_reg_set_enable(i, (u32)(mvi->sata_reg_set >> 32));
*tfs = i; *tfs = i;
...@@ -726,9 +731,12 @@ static void mvs_94xx_make_prd(struct scatterlist *scatter, int nr, void *prd) ...@@ -726,9 +731,12 @@ static void mvs_94xx_make_prd(struct scatterlist *scatter, int nr, void *prd)
int i; int i;
struct scatterlist *sg; struct scatterlist *sg;
struct mvs_prd *buf_prd = prd; struct mvs_prd *buf_prd = prd;
struct mvs_prd_imt im_len;
*(u32 *)&im_len = 0;
for_each_sg(scatter, sg, nr, i) { for_each_sg(scatter, sg, nr, i) {
buf_prd->addr = cpu_to_le64(sg_dma_address(sg)); buf_prd->addr = cpu_to_le64(sg_dma_address(sg));
buf_prd->im_len.len = cpu_to_le32(sg_dma_len(sg)); im_len.len = sg_dma_len(sg);
buf_prd->im_len = cpu_to_le32(*(u32 *)&im_len);
buf_prd++; buf_prd++;
} }
} }
...@@ -751,7 +759,7 @@ static void mvs_94xx_get_dev_identify_frame(struct mvs_info *mvi, int port_id, ...@@ -751,7 +759,7 @@ static void mvs_94xx_get_dev_identify_frame(struct mvs_info *mvi, int port_id,
for (i = 0; i < 7; i++) { for (i = 0; i < 7; i++) {
mvs_write_port_cfg_addr(mvi, port_id, mvs_write_port_cfg_addr(mvi, port_id,
CONFIG_ID_FRAME0 + i * 4); CONFIG_ID_FRAME0 + i * 4);
id_frame[i] = mvs_read_port_cfg_data(mvi, port_id); id_frame[i] = cpu_to_le32(mvs_read_port_cfg_data(mvi, port_id));
} }
memcpy(id, id_frame, 28); memcpy(id, id_frame, 28);
} }
...@@ -766,7 +774,7 @@ static void mvs_94xx_get_att_identify_frame(struct mvs_info *mvi, int port_id, ...@@ -766,7 +774,7 @@ static void mvs_94xx_get_att_identify_frame(struct mvs_info *mvi, int port_id,
for (i = 0; i < 7; i++) { for (i = 0; i < 7; i++) {
mvs_write_port_cfg_addr(mvi, port_id, mvs_write_port_cfg_addr(mvi, port_id,
CONFIG_ATT_ID_FRAME0 + i * 4); CONFIG_ATT_ID_FRAME0 + i * 4);
id_frame[i] = mvs_read_port_cfg_data(mvi, port_id); id_frame[i] = cpu_to_le32(mvs_read_port_cfg_data(mvi, port_id));
mv_dprintk("94xx phy %d atta frame %d %x.\n", mv_dprintk("94xx phy %d atta frame %d %x.\n",
port_id + mvi->id * mvi->chip->n_phy, i, id_frame[i]); port_id + mvi->id * mvi->chip->n_phy, i, id_frame[i]);
} }
...@@ -924,8 +932,12 @@ void mvs_94xx_fix_dma(struct mvs_info *mvi, u32 phy_mask, ...@@ -924,8 +932,12 @@ void mvs_94xx_fix_dma(struct mvs_info *mvi, u32 phy_mask,
int i; int i;
struct mvs_prd *buf_prd = prd; struct mvs_prd *buf_prd = prd;
dma_addr_t buf_dma; dma_addr_t buf_dma;
struct mvs_prd_imt im_len;
*(u32 *)&im_len = 0;
buf_prd += from; buf_prd += from;
#define PRD_CHAINED_ENTRY 0x01
if ((mvi->pdev->revision == VANIR_A0_REV) || if ((mvi->pdev->revision == VANIR_A0_REV) ||
(mvi->pdev->revision == VANIR_B0_REV)) (mvi->pdev->revision == VANIR_B0_REV))
buf_dma = (phy_mask <= 0x08) ? buf_dma = (phy_mask <= 0x08) ?
...@@ -933,10 +945,16 @@ void mvs_94xx_fix_dma(struct mvs_info *mvi, u32 phy_mask, ...@@ -933,10 +945,16 @@ void mvs_94xx_fix_dma(struct mvs_info *mvi, u32 phy_mask,
else else
return; return;
for (i = 0; i < MAX_SG_ENTRY - from; i++) { for (i = from; i < MAX_SG_ENTRY; i++, ++buf_prd) {
buf_prd->addr = cpu_to_le64(buf_dma); if (i == MAX_SG_ENTRY - 1) {
buf_prd->im_len.len = cpu_to_le32(buf_len); buf_prd->addr = cpu_to_le64(virt_to_phys(buf_prd - 1));
++buf_prd; im_len.len = 2;
im_len.misc_ctl = PRD_CHAINED_ENTRY;
} else {
buf_prd->addr = cpu_to_le64(buf_dma);
im_len.len = buf_len;
}
buf_prd->im_len = cpu_to_le32(*(u32 *)&im_len);
} }
} }
......
...@@ -142,8 +142,8 @@ enum sas_sata_vsp_regs { ...@@ -142,8 +142,8 @@ enum sas_sata_vsp_regs {
enum chip_register_bits { enum chip_register_bits {
PHY_MIN_SPP_PHYS_LINK_RATE_MASK = (0x7 << 8), PHY_MIN_SPP_PHYS_LINK_RATE_MASK = (0x7 << 8),
PHY_MAX_SPP_PHYS_LINK_RATE_MASK = (0x7 << 8), PHY_MAX_SPP_PHYS_LINK_RATE_MASK = (0x7 << 12),
PHY_NEG_SPP_PHYS_LINK_RATE_MASK_OFFSET = (12), PHY_NEG_SPP_PHYS_LINK_RATE_MASK_OFFSET = (16),
PHY_NEG_SPP_PHYS_LINK_RATE_MASK = PHY_NEG_SPP_PHYS_LINK_RATE_MASK =
(0x3 << PHY_NEG_SPP_PHYS_LINK_RATE_MASK_OFFSET), (0x3 << PHY_NEG_SPP_PHYS_LINK_RATE_MASK_OFFSET),
}; };
...@@ -219,17 +219,24 @@ union reg_phy_cfg { ...@@ -219,17 +219,24 @@ union reg_phy_cfg {
#define MAX_SG_ENTRY 255 #define MAX_SG_ENTRY 255
struct mvs_prd_imt { struct mvs_prd_imt {
#ifndef __BIG_ENDIAN
__le32 len:22; __le32 len:22;
u8 _r_a:2; u8 _r_a:2;
u8 misc_ctl:4; u8 misc_ctl:4;
u8 inter_sel:4; u8 inter_sel:4;
#else
u32 inter_sel:4;
u32 misc_ctl:4;
u32 _r_a:2;
u32 len:22;
#endif
}; };
struct mvs_prd { struct mvs_prd {
/* 64-bit buffer address */ /* 64-bit buffer address */
__le64 addr; __le64 addr;
/* 22-bit length */ /* 22-bit length */
struct mvs_prd_imt im_len; __le32 im_len;
} __attribute__ ((packed)); } __attribute__ ((packed));
/* /*
......
...@@ -184,18 +184,6 @@ static inline void __devinit mvs_phy_hacks(struct mvs_info *mvi) ...@@ -184,18 +184,6 @@ static inline void __devinit mvs_phy_hacks(struct mvs_info *mvi)
/* not to halt for different port op during wideport link change */ /* not to halt for different port op during wideport link change */
mvs_cw32(mvi, CMD_APP_ERR_CONFIG, 0xffefbf7d); mvs_cw32(mvi, CMD_APP_ERR_CONFIG, 0xffefbf7d);
/* workaround for Seagate disk not-found OOB sequence, recv
* COMINIT before sending out COMWAKE */
tmp = mvs_cr32(mvi, CMD_PHY_MODE_21);
tmp &= 0x0000ffff;
tmp |= 0x00fa0000;
mvs_cw32(mvi, CMD_PHY_MODE_21, tmp);
tmp = mvs_cr32(mvi, CMD_PHY_TIMER);
tmp &= 0x1fffffff;
tmp |= (2U << 29); /* 8 ms retry */
mvs_cw32(mvi, CMD_PHY_TIMER, tmp);
} }
static inline void mvs_int_sata(struct mvs_info *mvi) static inline void mvs_int_sata(struct mvs_info *mvi)
......
...@@ -104,6 +104,7 @@ static void __devinit mvs_phy_init(struct mvs_info *mvi, int phy_id) ...@@ -104,6 +104,7 @@ static void __devinit mvs_phy_init(struct mvs_info *mvi, int phy_id)
struct asd_sas_phy *sas_phy = &phy->sas_phy; struct asd_sas_phy *sas_phy = &phy->sas_phy;
phy->mvi = mvi; phy->mvi = mvi;
phy->port = NULL;
init_timer(&phy->timer); init_timer(&phy->timer);
sas_phy->enabled = (phy_id < mvi->chip->n_phy) ? 1 : 0; sas_phy->enabled = (phy_id < mvi->chip->n_phy) ? 1 : 0;
sas_phy->class = SAS; sas_phy->class = SAS;
...@@ -366,7 +367,7 @@ static struct mvs_info *__devinit mvs_pci_alloc(struct pci_dev *pdev, ...@@ -366,7 +367,7 @@ static struct mvs_info *__devinit mvs_pci_alloc(struct pci_dev *pdev,
const struct pci_device_id *ent, const struct pci_device_id *ent,
struct Scsi_Host *shost, unsigned int id) struct Scsi_Host *shost, unsigned int id)
{ {
struct mvs_info *mvi; struct mvs_info *mvi = NULL;
struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost); struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
mvi = kzalloc(sizeof(*mvi) + mvi = kzalloc(sizeof(*mvi) +
......
...@@ -300,7 +300,10 @@ int mvs_slave_configure(struct scsi_device *sdev) ...@@ -300,7 +300,10 @@ int mvs_slave_configure(struct scsi_device *sdev)
if (ret) if (ret)
return ret; return ret;
if (dev_is_sata(dev)) { if (!dev_is_sata(dev)) {
sas_change_queue_depth(sdev,
MVS_QUEUE_SIZE,
SCSI_QDEPTH_DEFAULT);
} }
return 0; return 0;
} }
...@@ -311,6 +314,7 @@ void mvs_scan_start(struct Scsi_Host *shost) ...@@ -311,6 +314,7 @@ void mvs_scan_start(struct Scsi_Host *shost)
unsigned short core_nr; unsigned short core_nr;
struct mvs_info *mvi; struct mvs_info *mvi;
struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost); struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
struct mvs_prv_info *mvs_prv = sha->lldd_ha;
core_nr = ((struct mvs_prv_info *)sha->lldd_ha)->n_host; core_nr = ((struct mvs_prv_info *)sha->lldd_ha)->n_host;
...@@ -319,15 +323,17 @@ void mvs_scan_start(struct Scsi_Host *shost) ...@@ -319,15 +323,17 @@ void mvs_scan_start(struct Scsi_Host *shost)
for (i = 0; i < mvi->chip->n_phy; ++i) for (i = 0; i < mvi->chip->n_phy; ++i)
mvs_bytes_dmaed(mvi, i); mvs_bytes_dmaed(mvi, i);
} }
mvs_prv->scan_finished = 1;
} }
int mvs_scan_finished(struct Scsi_Host *shost, unsigned long time) int mvs_scan_finished(struct Scsi_Host *shost, unsigned long time)
{ {
/* give the phy enabling interrupt event time to come in (1s struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
* is empirically about all it takes) */ struct mvs_prv_info *mvs_prv = sha->lldd_ha;
if (time < HZ)
if (mvs_prv->scan_finished == 0)
return 0; return 0;
/* Wait for discovery to finish */
scsi_flush_work(shost); scsi_flush_work(shost);
return 1; return 1;
} }
...@@ -625,6 +631,9 @@ static int mvs_task_prep_ssp(struct mvs_info *mvi, ...@@ -625,6 +631,9 @@ static int mvs_task_prep_ssp(struct mvs_info *mvi,
} }
if (is_tmf) if (is_tmf)
flags |= (MCH_SSP_FR_TASK << MCH_SSP_FR_TYPE_SHIFT); flags |= (MCH_SSP_FR_TASK << MCH_SSP_FR_TYPE_SHIFT);
else
flags |= (MCH_SSP_FR_CMD << MCH_SSP_FR_TYPE_SHIFT);
hdr->flags = cpu_to_le32(flags | (tei->n_elem << MCH_PRD_LEN_SHIFT)); hdr->flags = cpu_to_le32(flags | (tei->n_elem << MCH_PRD_LEN_SHIFT));
hdr->tags = cpu_to_le32(tag); hdr->tags = cpu_to_le32(tag);
hdr->data_len = cpu_to_le32(task->total_xfer_len); hdr->data_len = cpu_to_le32(task->total_xfer_len);
...@@ -1049,9 +1058,9 @@ static void mvs_slot_task_free(struct mvs_info *mvi, struct sas_task *task, ...@@ -1049,9 +1058,9 @@ static void mvs_slot_task_free(struct mvs_info *mvi, struct sas_task *task,
mvs_slot_free(mvi, slot_idx); mvs_slot_free(mvi, slot_idx);
} }
static void mvs_update_wideport(struct mvs_info *mvi, int i) static void mvs_update_wideport(struct mvs_info *mvi, int phy_no)
{ {
struct mvs_phy *phy = &mvi->phy[i]; struct mvs_phy *phy = &mvi->phy[phy_no];
struct mvs_port *port = phy->port; struct mvs_port *port = phy->port;
int j, no; int j, no;
...@@ -1106,16 +1115,16 @@ static void *mvs_get_d2h_reg(struct mvs_info *mvi, int i, void *buf) ...@@ -1106,16 +1115,16 @@ static void *mvs_get_d2h_reg(struct mvs_info *mvi, int i, void *buf)
return NULL; return NULL;
MVS_CHIP_DISP->write_port_cfg_addr(mvi, i, PHYR_SATA_SIG3); MVS_CHIP_DISP->write_port_cfg_addr(mvi, i, PHYR_SATA_SIG3);
s[3] = MVS_CHIP_DISP->read_port_cfg_data(mvi, i); s[3] = cpu_to_le32(MVS_CHIP_DISP->read_port_cfg_data(mvi, i));
MVS_CHIP_DISP->write_port_cfg_addr(mvi, i, PHYR_SATA_SIG2); MVS_CHIP_DISP->write_port_cfg_addr(mvi, i, PHYR_SATA_SIG2);
s[2] = MVS_CHIP_DISP->read_port_cfg_data(mvi, i); s[2] = cpu_to_le32(MVS_CHIP_DISP->read_port_cfg_data(mvi, i));
MVS_CHIP_DISP->write_port_cfg_addr(mvi, i, PHYR_SATA_SIG1); MVS_CHIP_DISP->write_port_cfg_addr(mvi, i, PHYR_SATA_SIG1);
s[1] = MVS_CHIP_DISP->read_port_cfg_data(mvi, i); s[1] = cpu_to_le32(MVS_CHIP_DISP->read_port_cfg_data(mvi, i));
MVS_CHIP_DISP->write_port_cfg_addr(mvi, i, PHYR_SATA_SIG0); MVS_CHIP_DISP->write_port_cfg_addr(mvi, i, PHYR_SATA_SIG0);
s[0] = MVS_CHIP_DISP->read_port_cfg_data(mvi, i); s[0] = cpu_to_le32(MVS_CHIP_DISP->read_port_cfg_data(mvi, i));
/* Workaround: take some ATAPI devices for ATA */ /* Workaround: take some ATAPI devices for ATA */
if (((s[1] & 0x00FFFFFF) == 0x00EB1401) && (*(u8 *)&s[3] == 0x01)) if (((s[1] & 0x00FFFFFF) == 0x00EB1401) && (*(u8 *)&s[3] == 0x01))
...@@ -1201,9 +1210,9 @@ void mvs_update_phyinfo(struct mvs_info *mvi, int i, int get_st) ...@@ -1201,9 +1210,9 @@ void mvs_update_phyinfo(struct mvs_info *mvi, int i, int get_st)
if (MVS_CHIP_DISP->phy_work_around) if (MVS_CHIP_DISP->phy_work_around)
MVS_CHIP_DISP->phy_work_around(mvi, i); MVS_CHIP_DISP->phy_work_around(mvi, i);
} }
mv_dprintk("port %d attach dev info is %x\n", mv_dprintk("phy %d attach dev info is %x\n",
i + mvi->id * mvi->chip->n_phy, phy->att_dev_info); i + mvi->id * mvi->chip->n_phy, phy->att_dev_info);
mv_dprintk("port %d attach sas addr is %llx\n", mv_dprintk("phy %d attach sas addr is %llx\n",
i + mvi->id * mvi->chip->n_phy, phy->att_dev_sas_addr); i + mvi->id * mvi->chip->n_phy, phy->att_dev_sas_addr);
out_done: out_done:
if (get_st) if (get_st)
...@@ -1228,10 +1237,10 @@ static void mvs_port_notify_formed(struct asd_sas_phy *sas_phy, int lock) ...@@ -1228,10 +1237,10 @@ static void mvs_port_notify_formed(struct asd_sas_phy *sas_phy, int lock)
} }
hi = i/((struct mvs_prv_info *)sas_ha->lldd_ha)->n_phy; hi = i/((struct mvs_prv_info *)sas_ha->lldd_ha)->n_phy;
mvi = ((struct mvs_prv_info *)sas_ha->lldd_ha)->mvi[hi]; mvi = ((struct mvs_prv_info *)sas_ha->lldd_ha)->mvi[hi];
if (sas_port->id >= mvi->chip->n_phy) if (i >= mvi->chip->n_phy)
port = &mvi->port[sas_port->id - mvi->chip->n_phy]; port = &mvi->port[i - mvi->chip->n_phy];
else else
port = &mvi->port[sas_port->id]; port = &mvi->port[i];
if (lock) if (lock)
spin_lock_irqsave(&mvi->lock, flags); spin_lock_irqsave(&mvi->lock, flags);
port->port_attached = 1; port->port_attached = 1;
...@@ -1260,7 +1269,7 @@ static void mvs_port_notify_deformed(struct asd_sas_phy *sas_phy, int lock) ...@@ -1260,7 +1269,7 @@ static void mvs_port_notify_deformed(struct asd_sas_phy *sas_phy, int lock)
return; return;
} }
list_for_each_entry(dev, &port->dev_list, dev_list_node) list_for_each_entry(dev, &port->dev_list, dev_list_node)
mvs_do_release_task(phy->mvi, phy_no, NULL); mvs_do_release_task(phy->mvi, phy_no, dev);
} }
...@@ -1324,6 +1333,7 @@ int mvs_dev_found_notify(struct domain_device *dev, int lock) ...@@ -1324,6 +1333,7 @@ int mvs_dev_found_notify(struct domain_device *dev, int lock)
mvi_device->dev_status = MVS_DEV_NORMAL; mvi_device->dev_status = MVS_DEV_NORMAL;
mvi_device->dev_type = dev->dev_type; mvi_device->dev_type = dev->dev_type;
mvi_device->mvi_info = mvi; mvi_device->mvi_info = mvi;
mvi_device->sas_device = dev;
if (parent_dev && DEV_IS_EXPANDER(parent_dev->dev_type)) { if (parent_dev && DEV_IS_EXPANDER(parent_dev->dev_type)) {
int phy_id; int phy_id;
u8 phy_num = parent_dev->ex_dev.num_phys; u8 phy_num = parent_dev->ex_dev.num_phys;
...@@ -1375,6 +1385,7 @@ void mvs_dev_gone_notify(struct domain_device *dev) ...@@ -1375,6 +1385,7 @@ void mvs_dev_gone_notify(struct domain_device *dev)
mv_dprintk("found dev has gone.\n"); mv_dprintk("found dev has gone.\n");
} }
dev->lldd_dev = NULL; dev->lldd_dev = NULL;
mvi_dev->sas_device = NULL;
spin_unlock_irqrestore(&mvi->lock, flags); spin_unlock_irqrestore(&mvi->lock, flags);
} }
...@@ -1455,7 +1466,7 @@ static int mvs_exec_internal_tmf_task(struct domain_device *dev, ...@@ -1455,7 +1466,7 @@ static int mvs_exec_internal_tmf_task(struct domain_device *dev,
} }
wait_for_completion(&task->completion); wait_for_completion(&task->completion);
res = -TMF_RESP_FUNC_FAILED; res = TMF_RESP_FUNC_FAILED;
/* Even TMF timed out, return direct. */ /* Even TMF timed out, return direct. */
if ((task->task_state_flags & SAS_TASK_STATE_ABORTED)) { if ((task->task_state_flags & SAS_TASK_STATE_ABORTED)) {
if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) { if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) {
...@@ -1505,11 +1516,10 @@ static int mvs_debug_issue_ssp_tmf(struct domain_device *dev, ...@@ -1505,11 +1516,10 @@ static int mvs_debug_issue_ssp_tmf(struct domain_device *dev,
u8 *lun, struct mvs_tmf_task *tmf) u8 *lun, struct mvs_tmf_task *tmf)
{ {
struct sas_ssp_task ssp_task; struct sas_ssp_task ssp_task;
DECLARE_COMPLETION_ONSTACK(completion);
if (!(dev->tproto & SAS_PROTOCOL_SSP)) if (!(dev->tproto & SAS_PROTOCOL_SSP))
return TMF_RESP_FUNC_ESUPP; return TMF_RESP_FUNC_ESUPP;
strncpy((u8 *)&ssp_task.LUN, lun, 8); memcpy(ssp_task.LUN, lun, 8);
return mvs_exec_internal_tmf_task(dev, &ssp_task, return mvs_exec_internal_tmf_task(dev, &ssp_task,
sizeof(ssp_task), tmf); sizeof(ssp_task), tmf);
...@@ -1533,7 +1543,7 @@ static int mvs_debug_I_T_nexus_reset(struct domain_device *dev) ...@@ -1533,7 +1543,7 @@ static int mvs_debug_I_T_nexus_reset(struct domain_device *dev)
int mvs_lu_reset(struct domain_device *dev, u8 *lun) int mvs_lu_reset(struct domain_device *dev, u8 *lun)
{ {
unsigned long flags; unsigned long flags;
int i, phyno[WIDE_PORT_MAX_PHY], num , rc = TMF_RESP_FUNC_FAILED; int rc = TMF_RESP_FUNC_FAILED;
struct mvs_tmf_task tmf_task; struct mvs_tmf_task tmf_task;
struct mvs_device * mvi_dev = dev->lldd_dev; struct mvs_device * mvi_dev = dev->lldd_dev;
struct mvs_info *mvi = mvi_dev->mvi_info; struct mvs_info *mvi = mvi_dev->mvi_info;
...@@ -1542,10 +1552,8 @@ int mvs_lu_reset(struct domain_device *dev, u8 *lun) ...@@ -1542,10 +1552,8 @@ int mvs_lu_reset(struct domain_device *dev, u8 *lun)
mvi_dev->dev_status = MVS_DEV_EH; mvi_dev->dev_status = MVS_DEV_EH;
rc = mvs_debug_issue_ssp_tmf(dev, lun, &tmf_task); rc = mvs_debug_issue_ssp_tmf(dev, lun, &tmf_task);
if (rc == TMF_RESP_FUNC_COMPLETE) { if (rc == TMF_RESP_FUNC_COMPLETE) {
num = mvs_find_dev_phyno(dev, phyno);
spin_lock_irqsave(&mvi->lock, flags); spin_lock_irqsave(&mvi->lock, flags);
for (i = 0; i < num; i++) mvs_release_task(mvi, dev);
mvs_release_task(mvi, dev);
spin_unlock_irqrestore(&mvi->lock, flags); spin_unlock_irqrestore(&mvi->lock, flags);
} }
/* If failed, fall-through I_T_Nexus reset */ /* If failed, fall-through I_T_Nexus reset */
...@@ -1563,6 +1571,8 @@ int mvs_I_T_nexus_reset(struct domain_device *dev) ...@@ -1563,6 +1571,8 @@ int mvs_I_T_nexus_reset(struct domain_device *dev)
if (mvi_dev->dev_status != MVS_DEV_EH) if (mvi_dev->dev_status != MVS_DEV_EH)
return TMF_RESP_FUNC_COMPLETE; return TMF_RESP_FUNC_COMPLETE;
else
mvi_dev->dev_status = MVS_DEV_NORMAL;
rc = mvs_debug_I_T_nexus_reset(dev); rc = mvs_debug_I_T_nexus_reset(dev);
mv_printk("%s for device[%x]:rc= %d\n", mv_printk("%s for device[%x]:rc= %d\n",
__func__, mvi_dev->device_id, rc); __func__, mvi_dev->device_id, rc);
...@@ -1606,9 +1616,6 @@ int mvs_query_task(struct sas_task *task) ...@@ -1606,9 +1616,6 @@ int mvs_query_task(struct sas_task *task)
case TMF_RESP_FUNC_FAILED: case TMF_RESP_FUNC_FAILED:
case TMF_RESP_FUNC_COMPLETE: case TMF_RESP_FUNC_COMPLETE:
break; break;
default:
rc = TMF_RESP_FUNC_COMPLETE;
break;
} }
} }
mv_printk("%s:rc= %d\n", __func__, rc); mv_printk("%s:rc= %d\n", __func__, rc);
...@@ -1628,8 +1635,8 @@ int mvs_abort_task(struct sas_task *task) ...@@ -1628,8 +1635,8 @@ int mvs_abort_task(struct sas_task *task)
u32 tag; u32 tag;
if (!mvi_dev) { if (!mvi_dev) {
mv_printk("%s:%d TMF_RESP_FUNC_FAILED\n", __func__, __LINE__); mv_printk("Device has removed\n");
rc = TMF_RESP_FUNC_FAILED; return TMF_RESP_FUNC_FAILED;
} }
mvi = mvi_dev->mvi_info; mvi = mvi_dev->mvi_info;
...@@ -1677,22 +1684,15 @@ int mvs_abort_task(struct sas_task *task) ...@@ -1677,22 +1684,15 @@ int mvs_abort_task(struct sas_task *task)
/* to do free register_set */ /* to do free register_set */
if (SATA_DEV == dev->dev_type) { if (SATA_DEV == dev->dev_type) {
struct mvs_slot_info *slot = task->lldd_task; struct mvs_slot_info *slot = task->lldd_task;
struct task_status_struct *tstat;
u32 slot_idx = (u32)(slot - mvi->slot_info); u32 slot_idx = (u32)(slot - mvi->slot_info);
tstat = &task->task_status; mv_dprintk("mvs_abort_task() mvi=%p task=%p "
mv_dprintk(KERN_DEBUG "mv_abort_task() mvi=%p task=%p "
"slot=%p slot_idx=x%x\n", "slot=%p slot_idx=x%x\n",
mvi, task, slot, slot_idx); mvi, task, slot, slot_idx);
tstat->stat = SAS_ABORTED_TASK; mvs_tmf_timedout((unsigned long)task);
if (mvi_dev && mvi_dev->running_req)
mvi_dev->running_req--;
if (sas_protocol_ata(task->task_proto))
mvs_free_reg_set(mvi, mvi_dev);
mvs_slot_task_free(mvi, task, slot, slot_idx); mvs_slot_task_free(mvi, task, slot, slot_idx);
return -1; rc = TMF_RESP_FUNC_COMPLETE;
goto out;
} }
} else {
/* SMP */
} }
out: out:
...@@ -1813,7 +1813,7 @@ static int mvs_slot_err(struct mvs_info *mvi, struct sas_task *task, ...@@ -1813,7 +1813,7 @@ static int mvs_slot_err(struct mvs_info *mvi, struct sas_task *task,
{ {
struct mvs_slot_info *slot = &mvi->slot_info[slot_idx]; struct mvs_slot_info *slot = &mvi->slot_info[slot_idx];
int stat; int stat;
u32 err_dw0 = le32_to_cpu(*(u32 *) (slot->response)); u32 err_dw0 = le32_to_cpu(*(u32 *)slot->response);
u32 err_dw1 = le32_to_cpu(*((u32 *)slot->response + 1)); u32 err_dw1 = le32_to_cpu(*((u32 *)slot->response + 1));
u32 tfs = 0; u32 tfs = 0;
enum mvs_port_type type = PORT_TYPE_SAS; enum mvs_port_type type = PORT_TYPE_SAS;
...@@ -1847,10 +1847,8 @@ static int mvs_slot_err(struct mvs_info *mvi, struct sas_task *task, ...@@ -1847,10 +1847,8 @@ static int mvs_slot_err(struct mvs_info *mvi, struct sas_task *task,
case SAS_PROTOCOL_STP: case SAS_PROTOCOL_STP:
case SAS_PROTOCOL_SATA | SAS_PROTOCOL_STP: case SAS_PROTOCOL_SATA | SAS_PROTOCOL_STP:
{ {
if (err_dw0 == 0x80400002)
mv_printk("find reserved error, why?\n");
task->ata_task.use_ncq = 0; task->ata_task.use_ncq = 0;
stat = SAS_PROTO_RESPONSE;
mvs_sata_done(mvi, task, slot_idx, err_dw0); mvs_sata_done(mvi, task, slot_idx, err_dw0);
} }
break; break;
...@@ -1912,6 +1910,9 @@ int mvs_slot_complete(struct mvs_info *mvi, u32 rx_desc, u32 flags) ...@@ -1912,6 +1910,9 @@ int mvs_slot_complete(struct mvs_info *mvi, u32 rx_desc, u32 flags)
/* error info record present */ /* error info record present */
if (unlikely((rx_desc & RXQ_ERR) && (*(u64 *) slot->response))) { if (unlikely((rx_desc & RXQ_ERR) && (*(u64 *) slot->response))) {
mv_dprintk("port %d slot %d rx_desc %X has error info"
"%016llX.\n", slot->port->sas_port.id, slot_idx,
rx_desc, (u64)(*(u64 *)slot->response));
tstat->stat = mvs_slot_err(mvi, task, slot_idx); tstat->stat = mvs_slot_err(mvi, task, slot_idx);
tstat->resp = SAS_TASK_COMPLETE; tstat->resp = SAS_TASK_COMPLETE;
goto out; goto out;
...@@ -1973,8 +1974,7 @@ int mvs_slot_complete(struct mvs_info *mvi, u32 rx_desc, u32 flags) ...@@ -1973,8 +1974,7 @@ int mvs_slot_complete(struct mvs_info *mvi, u32 rx_desc, u32 flags)
spin_unlock(&mvi->lock); spin_unlock(&mvi->lock);
if (task->task_done) if (task->task_done)
task->task_done(task); task->task_done(task);
else
mv_dprintk("why has not task_done.\n");
spin_lock(&mvi->lock); spin_lock(&mvi->lock);
return sts; return sts;
...@@ -2115,9 +2115,10 @@ void mvs_int_port(struct mvs_info *mvi, int phy_no, u32 events) ...@@ -2115,9 +2115,10 @@ void mvs_int_port(struct mvs_info *mvi, int phy_no, u32 events)
struct mvs_phy *phy = &mvi->phy[phy_no]; struct mvs_phy *phy = &mvi->phy[phy_no];
phy->irq_status = MVS_CHIP_DISP->read_port_irq_stat(mvi, phy_no); phy->irq_status = MVS_CHIP_DISP->read_port_irq_stat(mvi, phy_no);
mv_dprintk("port %d ctrl sts=0x%X.\n", phy_no+mvi->id*mvi->chip->n_phy, MVS_CHIP_DISP->write_port_irq_stat(mvi, phy_no, phy->irq_status);
mv_dprintk("phy %d ctrl sts=0x%08X.\n", phy_no+mvi->id*mvi->chip->n_phy,
MVS_CHIP_DISP->read_phy_ctl(mvi, phy_no)); MVS_CHIP_DISP->read_phy_ctl(mvi, phy_no));
mv_dprintk("Port %d irq sts = 0x%X\n", phy_no+mvi->id*mvi->chip->n_phy, mv_dprintk("phy %d irq sts = 0x%08X\n", phy_no+mvi->id*mvi->chip->n_phy,
phy->irq_status); phy->irq_status);
/* /*
...@@ -2126,11 +2127,12 @@ void mvs_int_port(struct mvs_info *mvi, int phy_no, u32 events) ...@@ -2126,11 +2127,12 @@ void mvs_int_port(struct mvs_info *mvi, int phy_no, u32 events)
*/ */
if (phy->irq_status & PHYEV_DCDR_ERR) { if (phy->irq_status & PHYEV_DCDR_ERR) {
mv_dprintk("port %d STP decoding error.\n", mv_dprintk("phy %d STP decoding error.\n",
phy_no + mvi->id*mvi->chip->n_phy); phy_no + mvi->id*mvi->chip->n_phy);
} }
if (phy->irq_status & PHYEV_POOF) { if (phy->irq_status & PHYEV_POOF) {
mdelay(500);
if (!(phy->phy_event & PHY_PLUG_OUT)) { if (!(phy->phy_event & PHY_PLUG_OUT)) {
int dev_sata = phy->phy_type & PORT_TYPE_SATA; int dev_sata = phy->phy_type & PORT_TYPE_SATA;
int ready; int ready;
...@@ -2141,10 +2143,6 @@ void mvs_int_port(struct mvs_info *mvi, int phy_no, u32 events) ...@@ -2141,10 +2143,6 @@ void mvs_int_port(struct mvs_info *mvi, int phy_no, u32 events)
(void *)(unsigned long)phy_no, (void *)(unsigned long)phy_no,
PHY_PLUG_EVENT); PHY_PLUG_EVENT);
ready = mvs_is_phy_ready(mvi, phy_no); ready = mvs_is_phy_ready(mvi, phy_no);
if (!ready)
mv_dprintk("phy%d Unplug Notice\n",
phy_no +
mvi->id * mvi->chip->n_phy);
if (ready || dev_sata) { if (ready || dev_sata) {
if (MVS_CHIP_DISP->stp_reset) if (MVS_CHIP_DISP->stp_reset)
MVS_CHIP_DISP->stp_reset(mvi, MVS_CHIP_DISP->stp_reset(mvi,
...@@ -2164,7 +2162,7 @@ void mvs_int_port(struct mvs_info *mvi, int phy_no, u32 events) ...@@ -2164,7 +2162,7 @@ void mvs_int_port(struct mvs_info *mvi, int phy_no, u32 events)
if (phy->timer.function == NULL) { if (phy->timer.function == NULL) {
phy->timer.data = (unsigned long)phy; phy->timer.data = (unsigned long)phy;
phy->timer.function = mvs_sig_time_out; phy->timer.function = mvs_sig_time_out;
phy->timer.expires = jiffies + 10*HZ; phy->timer.expires = jiffies + 5*HZ;
add_timer(&phy->timer); add_timer(&phy->timer);
} }
} }
...@@ -2198,12 +2196,11 @@ void mvs_int_port(struct mvs_info *mvi, int phy_no, u32 events) ...@@ -2198,12 +2196,11 @@ void mvs_int_port(struct mvs_info *mvi, int phy_no, u32 events)
phy_no + mvi->id*mvi->chip->n_phy); phy_no + mvi->id*mvi->chip->n_phy);
} }
} else if (phy->irq_status & PHYEV_BROAD_CH) { } else if (phy->irq_status & PHYEV_BROAD_CH) {
mv_dprintk("port %d broadcast change.\n", mv_dprintk("phy %d broadcast change.\n",
phy_no + mvi->id*mvi->chip->n_phy); phy_no + mvi->id*mvi->chip->n_phy);
mvs_handle_event(mvi, (void *)(unsigned long)phy_no, mvs_handle_event(mvi, (void *)(unsigned long)phy_no,
EXP_BRCT_CHG); EXP_BRCT_CHG);
} }
MVS_CHIP_DISP->write_port_irq_stat(mvi, phy_no, phy->irq_status);
} }
int mvs_int_rx(struct mvs_info *mvi, bool self_clear) int mvs_int_rx(struct mvs_info *mvi, bool self_clear)
......
...@@ -417,7 +417,8 @@ struct mvs_info { ...@@ -417,7 +417,8 @@ struct mvs_info {
struct mvs_prv_info{ struct mvs_prv_info{
u8 n_host; u8 n_host;
u8 n_phy; u8 n_phy;
u16 reserve; u8 scan_finished;
u8 reserve;
struct mvs_info *mvi[2]; struct mvs_info *mvi[2];
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment