/*
* Marvell 88SE64xx hardware specific
*
* Copyright 2007 Red Hat, Inc.
* Copyright 2008 Marvell. <kewei@marvell.com>
* Copyright 2009-2011 Marvell. <yuxiangl@marvell.com>
*
* This file is licensed under GPLv2.
*
* 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; version 2 of the
* License.
*
* 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; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA
*/
#include "mv_sas.h"
#include "mv_64xx.h"
#include "mv_chips.h"
static void mvs_64xx_detect_porttype(struct mvs_info *mvi, int i)
{
void __iomem *regs = mvi->regs;
u32 reg;
struct mvs_phy *phy = &mvi->phy[i];
reg = mr32(MVS_GBL_PORT_TYPE);
phy->phy_type &= ~(PORT_TYPE_SAS | PORT_TYPE_SATA);
if (reg & MODE_SAS_SATA & (1 << i))
phy->phy_type |= PORT_TYPE_SAS;
else
phy->phy_type |= PORT_TYPE_SATA;
}
static void mvs_64xx_enable_xmt(struct mvs_info *mvi, int phy_id)
{
void __iomem *regs = mvi->regs;
u32 tmp;
tmp = mr32(MVS_PCS);
if (mvi->chip->n_phy <= MVS_SOC_PORTS)
tmp |= 1 << (phy_id + PCS_EN_PORT_XMT_SHIFT);
else
tmp |= 1 << (phy_id + PCS_EN_PORT_XMT_SHIFT2);
mw32(MVS_PCS, tmp);
}
static void mvs_64xx_phy_hacks(struct mvs_info *mvi)
{
void __iomem *regs = mvi->regs;
int i;
mvs_phy_hacks(mvi);
if (!(mvi->flags & MVF_FLAG_SOC)) {
for (i = 0; i < MVS_SOC_PORTS; i++) {
mvs_write_port_vsr_addr(mvi, i, VSR_PHY_MODE8);
mvs_write_port_vsr_data(mvi, i, 0x2F0);
}
} else {
/* disable auto port detection */
mw32(MVS_GBL_PORT_TYPE, 0);
for (i = 0; i < mvi->chip->n_phy; i++) {
mvs_write_port_vsr_addr(mvi, i, VSR_PHY_MODE7);
mvs_write_port_vsr_data(mvi, i, 0x90000000);
mvs_write_port_vsr_addr(mvi, i, VSR_PHY_MODE9);
mvs_write_port_vsr_data(mvi, i, 0x50f2);
mvs_write_port_vsr_addr(mvi, i, VSR_PHY_MODE11);
mvs_write_port_vsr_data(mvi, i, 0x0e);
}
}
}
static void mvs_64xx_stp_reset(struct mvs_info *mvi, u32 phy_id)
{
void __iomem *regs = mvi->regs;
u32 reg, tmp;
if (!(mvi->flags & MVF_FLAG_SOC)) {
if (phy_id < MVS_SOC_PORTS)
pci_read_config_dword(mvi->pdev, PCR_PHY_CTL, ®);
else
pci_read_config_dword(mvi->pdev, PCR_PHY_CTL2, ®);
} else
reg = mr32(MVS_PHY_CTL);
tmp = reg;
if (phy_id < MVS_SOC_PORTS)
tmp |= (1U << phy_id) << PCTL_LINK_OFFS;
else
tmp |= (1U << (phy_id - MVS_SOC_PORTS)) << PCTL_LINK_OFFS;
if (!(mvi->flags & MVF_FLAG_SOC)) {
if (phy_id < MVS_SOC_PORTS) {
pci_write_config_dword(mvi->pdev, PCR_PHY_CTL, tmp);
mdelay(10);
pci_write_config_dword(mvi->pdev, PCR_PHY_CTL, reg);
} else {
pci_write_config_dword(mvi->pdev, PCR_PHY_CTL2, tmp);
mdelay(10);
pci_write_config_dword(mvi->pdev, PCR_PHY_CTL2, reg);
}
} else {
mw32(MVS_PHY_CTL, tmp);
mdelay(10);
mw32(MVS_PHY_CTL, reg);
}
}
static void mvs_64xx_phy_reset(struct mvs_info *mvi, u32 phy_id, int hard)
{
u32 tmp;
tmp = mvs_read_port_irq_stat(mvi, phy_id);
tmp &= ~PHYEV_RDY_CH;
mvs_write_port_irq_stat(mvi, phy_id, tmp);
tmp = mvs_read_phy_ctl(mvi, phy_id);
if (hard == MVS_HARD_RESET)
tmp |= PHY_RST_HARD;
else if (hard == MVS_SOFT_RESET)
tmp |= PHY_RST;
mvs_write_phy_ctl(mvi, phy_id, tmp);
if (hard) {
do {
tmp = mvs_read_phy_ctl(mvi, phy_id);
} while (tmp & PHY_RST_HARD);
}
}
static void
mvs_64xx_clear_srs_irq(struct mvs_info *mvi, u8 reg_set, u8 clear_all)
{
void __iomem *regs = mvi->regs;
u32 tmp;
if (clear_all) {
tmp = mr32(MVS_INT_STAT_SRS_0);
if (tmp) {
printk(KERN_DEBUG "check SRS 0 %08X.\n", tmp);
mw32(MVS_INT_STAT_SRS_0, tmp);
}
} else {
tmp = mr32(MVS_INT_STAT_SRS_0);
if (tmp & (1 << (reg_set % 32))) {
printk(KERN_DEBUG "register set 0x%x wa