| /* |
| 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; |
| } |
| |