mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-10-29 23:53:32 +00:00
185 lines
4.1 KiB
C
185 lines
4.1 KiB
C
|
/*
|
||
|
mv_64xx.c - Marvell 88SE6440 SAS/SATA support
|
||
|
|
||
|
Copyright 2007 Red Hat, Inc.
|
||
|
Copyright 2008 Marvell. <kewei@marvell.com>
|
||
|
|
||
|
This program is free software; you can redistribute it and/or
|
||
|
modify it under the terms of the GNU General Public License as
|
||
|
published by the Free Software Foundation; either version 2,
|
||
|
or (at your option) any later version.
|
||
|
|
||
|
This program is distributed in the hope that it will be useful,
|
||
|
but WITHOUT ANY WARRANTY; without even the implied warranty
|
||
|
of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||
|
See the GNU General Public License for more details.
|
||
|
|
||
|
You should have received a copy of the GNU General Public
|
||
|
License along with this program; see the file COPYING. If not,
|
||
|
write to the Free Software Foundation, 675 Mass Ave, Cambridge,
|
||
|
MA 02139, USA.
|
||
|
|
||
|
*/
|
||
|
|
||
|
#include "mv_sas.h"
|
||
|
#include "mv_64xx.h"
|
||
|
#include "mv_chips.h"
|
||
|
|
||
|
void mvs_detect_porttype(struct mvs_info *mvi, int i)
|
||
|
{
|
||
|
void __iomem *regs = mvi->regs;
|
||
|
u32 reg;
|
||
|
struct mvs_phy *phy = &mvi->phy[i];
|
||
|
|
||
|
/* TODO check & save device type */
|
||
|
reg = mr32(GBL_PORT_TYPE);
|
||
|
|
||
|
if (reg & MODE_SAS_SATA & (1 << i))
|
||
|
phy->phy_type |= PORT_TYPE_SAS;
|
||
|
else
|
||
|
phy->phy_type |= PORT_TYPE_SATA;
|
||
|
}
|
||
|
|
||
|
void mvs_enable_xmt(struct mvs_info *mvi, int PhyId)
|
||
|
{
|
||
|
void __iomem *regs = mvi->regs;
|
||
|
u32 tmp;
|
||
|
|
||
|
tmp = mr32(PCS);
|
||
|
if (mvi->chip->n_phy <= 4)
|
||
|
tmp |= 1 << (PhyId + PCS_EN_PORT_XMT_SHIFT);
|
||
|
else
|
||
|
tmp |= 1 << (PhyId + PCS_EN_PORT_XMT_SHIFT2);
|
||
|
mw32(PCS, tmp);
|
||
|
}
|
||
|
|
||
|
void __devinit mvs_phy_hacks(struct mvs_info *mvi)
|
||
|
{
|
||
|
void __iomem *regs = mvi->regs;
|
||
|
u32 tmp;
|
||
|
|
||
|
/* workaround for SATA R-ERR, to ignore phy glitch */
|
||
|
tmp = mvs_cr32(regs, CMD_PHY_TIMER);
|
||
|
tmp &= ~(1 << 9);
|
||
|
tmp |= (1 << 10);
|
||
|
mvs_cw32(regs, CMD_PHY_TIMER, tmp);
|
||
|
|
||
|
/* enable retry 127 times */
|
||
|
mvs_cw32(regs, CMD_SAS_CTL1, 0x7f7f);
|
||
|
|
||
|
/* extend open frame timeout to max */
|
||
|
tmp = mvs_cr32(regs, CMD_SAS_CTL0);
|
||
|
tmp &= ~0xffff;
|
||
|
tmp |= 0x3fff;
|
||
|
mvs_cw32(regs, CMD_SAS_CTL0, tmp);
|
||
|
|
||
|
/* workaround for WDTIMEOUT , set to 550 ms */
|
||
|
mvs_cw32(regs, CMD_WD_TIMER, 0x86470);
|
||
|
|
||
|
/* not to halt for different port op during wideport link change */
|
||
|
mvs_cw32(regs, CMD_APP_ERR_CONFIG, 0xffefbf7d);
|
||
|
|
||
|
/* workaround for Seagate disk not-found OOB sequence, recv
|
||
|
* COMINIT before sending out COMWAKE */
|
||
|
tmp = mvs_cr32(regs, CMD_PHY_MODE_21);
|
||
|
tmp &= 0x0000ffff;
|
||
|
tmp |= 0x00fa0000;
|
||
|
mvs_cw32(regs, CMD_PHY_MODE_21, tmp);
|
||
|
|
||
|
tmp = mvs_cr32(regs, CMD_PHY_TIMER);
|
||
|
tmp &= 0x1fffffff;
|
||
|
tmp |= (2U << 29); /* 8 ms retry */
|
||
|
mvs_cw32(regs, CMD_PHY_TIMER, tmp);
|
||
|
|
||
|
/* TEST - for phy decoding error, adjust voltage levels */
|
||
|
mw32(P0_VSR_ADDR + 0, 0x8);
|
||
|
mw32(P0_VSR_DATA + 0, 0x2F0);
|
||
|
|
||
|
mw32(P0_VSR_ADDR + 8, 0x8);
|
||
|
mw32(P0_VSR_DATA + 8, 0x2F0);
|
||
|
|
||
|
mw32(P0_VSR_ADDR + 16, 0x8);
|
||
|
mw32(P0_VSR_DATA + 16, 0x2F0);
|
||
|
|
||
|
mw32(P0_VSR_ADDR + 24, 0x8);
|
||
|
mw32(P0_VSR_DATA + 24, 0x2F0);
|
||
|
|
||
|
}
|
||
|
|
||
|
void mvs_hba_interrupt_enable(struct mvs_info *mvi)
|
||
|
{
|
||
|
void __iomem *regs = mvi->regs;
|
||
|
u32 tmp;
|
||
|
|
||
|
tmp = mr32(GBL_CTL);
|
||
|
|
||
|
mw32(GBL_CTL, tmp | INT_EN);
|
||
|
}
|
||
|
|
||
|
void mvs_hba_interrupt_disable(struct mvs_info *mvi)
|
||
|
{
|
||
|
void __iomem *regs = mvi->regs;
|
||
|
u32 tmp;
|
||
|
|
||
|
tmp = mr32(GBL_CTL);
|
||
|
|
||
|
mw32(GBL_CTL, tmp & ~INT_EN);
|
||
|
}
|
||
|
|
||
|
void mvs_free_reg_set(struct mvs_info *mvi, struct mvs_port *port)
|
||
|
{
|
||
|
void __iomem *regs = mvi->regs;
|
||
|
u32 tmp, offs;
|
||
|
u8 *tfs = &port->taskfileset;
|
||
|
|
||
|
if (*tfs == MVS_ID_NOT_MAPPED)
|
||
|
return;
|
||
|
|
||
|
offs = 1U << ((*tfs & 0x0f) + PCS_EN_SATA_REG_SHIFT);
|
||
|
if (*tfs < 16) {
|
||
|
tmp = mr32(PCS);
|
||
|
mw32(PCS, tmp & ~offs);
|
||
|
} else {
|
||
|
tmp = mr32(CTL);
|
||
|
mw32(CTL, tmp & ~offs);
|
||
|
}
|
||
|
|
||
|
tmp = mr32(INT_STAT_SRS) & (1U << *tfs);
|
||
|
if (tmp)
|
||
|
mw32(INT_STAT_SRS, tmp);
|
||
|
|
||
|
*tfs = MVS_ID_NOT_MAPPED;
|
||
|
}
|
||
|
|
||
|
u8 mvs_assign_reg_set(struct mvs_info *mvi, struct mvs_port *port)
|
||
|
{
|
||
|
int i;
|
||
|
u32 tmp, offs;
|
||
|
void __iomem *regs = mvi->regs;
|
||
|
|
||
|
if (port->taskfileset != MVS_ID_NOT_MAPPED)
|
||
|
return 0;
|
||
|
|
||
|
tmp = mr32(PCS);
|
||
|
|
||
|
for (i = 0; i < mvi->chip->srs_sz; i++) {
|
||
|
if (i == 16)
|
||
|
tmp = mr32(CTL);
|
||
|
offs = 1U << ((i & 0x0f) + PCS_EN_SATA_REG_SHIFT);
|
||
|
if (!(tmp & offs)) {
|
||
|
port->taskfileset = i;
|
||
|
|
||
|
if (i < 16)
|
||
|
mw32(PCS, tmp | offs);
|
||
|
else
|
||
|
mw32(CTL, tmp | offs);
|
||
|
tmp = mr32(INT_STAT_SRS) & (1U << i);
|
||
|
if (tmp)
|
||
|
mw32(INT_STAT_SRS, tmp);
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
return MVS_ID_NOT_MAPPED;
|
||
|
}
|
||
|
|