Commit 9e3cb294 authored by Victor Gallardo's avatar Victor Gallardo Committed by Josh Boyer

ibm_newemac: Add support for GPCS, SGMII and M88E1112 PHY

Add support for the phy types found on the Arches and other
PowerPC 460 based boards.
Signed-off-by: default avatarVictor Gallardo <vgallardo@amcc.com>
Acked-by: default avatarBenjamin Herrenschmidt <benh@kernel.crashing.org>
Acked-by: default avatarJeff Garzik <jeff@garzik.org>
Signed-off-by: default avatarJosh Boyer <jwboyer@linux.vnet.ibm.com>
parent 5a013fc7
...@@ -75,6 +75,10 @@ ...@@ -75,6 +75,10 @@
#define ICINTSTAT_ICTX1 0x20000000 #define ICINTSTAT_ICTX1 0x20000000
#define ICINTSTAT_ICTX 0x60000000 #define ICINTSTAT_ICTX 0x60000000
/* SDRs (460EX/460GT) */
#define SDR0_ETH_CFG 0x4103
#define SDR0_ETH_CFG_ECS 0x00000100 /* EMAC int clk source */
/* /*
* All those DCR register addresses are offsets from the base address * All those DCR register addresses are offsets from the base address
* for the SRAM0 controller (e.g. 0x20 on 440GX). The base address is * for the SRAM0 controller (e.g. 0x20 on 440GX). The base address is
......
...@@ -130,6 +130,7 @@ static inline void emac_report_timeout_error(struct emac_instance *dev, ...@@ -130,6 +130,7 @@ static inline void emac_report_timeout_error(struct emac_instance *dev,
const char *error) const char *error)
{ {
if (emac_has_feature(dev, EMAC_FTR_440GX_PHY_CLK_FIX | if (emac_has_feature(dev, EMAC_FTR_440GX_PHY_CLK_FIX |
EMAC_FTR_460EX_PHY_CLK_FIX |
EMAC_FTR_440EP_PHY_CLK_FIX)) EMAC_FTR_440EP_PHY_CLK_FIX))
DBG(dev, "%s" NL, error); DBG(dev, "%s" NL, error);
else if (net_ratelimit()) else if (net_ratelimit())
...@@ -201,13 +202,15 @@ static inline int emac_phy_supports_gige(int phy_mode) ...@@ -201,13 +202,15 @@ static inline int emac_phy_supports_gige(int phy_mode)
{ {
return phy_mode == PHY_MODE_GMII || return phy_mode == PHY_MODE_GMII ||
phy_mode == PHY_MODE_RGMII || phy_mode == PHY_MODE_RGMII ||
phy_mode == PHY_MODE_SGMII ||
phy_mode == PHY_MODE_TBI || phy_mode == PHY_MODE_TBI ||
phy_mode == PHY_MODE_RTBI; phy_mode == PHY_MODE_RTBI;
} }
static inline int emac_phy_gpcs(int phy_mode) static inline int emac_phy_gpcs(int phy_mode)
{ {
return phy_mode == PHY_MODE_TBI || return phy_mode == PHY_MODE_SGMII ||
phy_mode == PHY_MODE_TBI ||
phy_mode == PHY_MODE_RTBI; phy_mode == PHY_MODE_RTBI;
} }
...@@ -351,10 +354,24 @@ static int emac_reset(struct emac_instance *dev) ...@@ -351,10 +354,24 @@ static int emac_reset(struct emac_instance *dev)
emac_tx_disable(dev); emac_tx_disable(dev);
} }
#ifdef CONFIG_PPC_DCR_NATIVE
/* Enable internal clock source */
if (emac_has_feature(dev, EMAC_FTR_460EX_PHY_CLK_FIX))
dcri_clrset(SDR0, SDR0_ETH_CFG,
0, SDR0_ETH_CFG_ECS << dev->cell_index);
#endif
out_be32(&p->mr0, EMAC_MR0_SRST); out_be32(&p->mr0, EMAC_MR0_SRST);
while ((in_be32(&p->mr0) & EMAC_MR0_SRST) && n) while ((in_be32(&p->mr0) & EMAC_MR0_SRST) && n)
--n; --n;
#ifdef CONFIG_PPC_DCR_NATIVE
/* Enable external clock source */
if (emac_has_feature(dev, EMAC_FTR_460EX_PHY_CLK_FIX))
dcri_clrset(SDR0, SDR0_ETH_CFG,
SDR0_ETH_CFG_ECS << dev->cell_index, 0);
#endif
if (n) { if (n) {
dev->reset_failed = 0; dev->reset_failed = 0;
return 0; return 0;
...@@ -547,8 +564,9 @@ static int emac_configure(struct emac_instance *dev) ...@@ -547,8 +564,9 @@ static int emac_configure(struct emac_instance *dev)
switch (dev->phy.speed) { switch (dev->phy.speed) {
case SPEED_1000: case SPEED_1000:
if (emac_phy_gpcs(dev->phy.mode)) { if (emac_phy_gpcs(dev->phy.mode)) {
mr1 |= EMAC_MR1_MF_1000GPCS | mr1 |= EMAC_MR1_MF_1000GPCS | EMAC_MR1_MF_IPPA(
EMAC_MR1_MF_IPPA(dev->phy.address); (dev->phy.gpcs_address != 0xffffffff) ?
dev->phy.gpcs_address : dev->phy.address);
/* Put some arbitrary OUI, Manuf & Rev IDs so we can /* Put some arbitrary OUI, Manuf & Rev IDs so we can
* identify this GPCS PHY later. * identify this GPCS PHY later.
...@@ -660,8 +678,12 @@ static int emac_configure(struct emac_instance *dev) ...@@ -660,8 +678,12 @@ static int emac_configure(struct emac_instance *dev)
out_be32(&p->iser, r); out_be32(&p->iser, r);
/* We need to take GPCS PHY out of isolate mode after EMAC reset */ /* We need to take GPCS PHY out of isolate mode after EMAC reset */
if (emac_phy_gpcs(dev->phy.mode)) if (emac_phy_gpcs(dev->phy.mode)) {
emac_mii_reset_phy(&dev->phy); if (dev->phy.gpcs_address != 0xffffffff)
emac_mii_reset_gpcs(&dev->phy);
else
emac_mii_reset_phy(&dev->phy);
}
return 0; return 0;
} }
...@@ -866,7 +888,9 @@ static int emac_mdio_read(struct net_device *ndev, int id, int reg) ...@@ -866,7 +888,9 @@ static int emac_mdio_read(struct net_device *ndev, int id, int reg)
struct emac_instance *dev = netdev_priv(ndev); struct emac_instance *dev = netdev_priv(ndev);
int res; int res;
res = __emac_mdio_read(dev->mdio_instance ? dev->mdio_instance : dev, res = __emac_mdio_read((dev->mdio_instance &&
dev->phy.gpcs_address != id) ?
dev->mdio_instance : dev,
(u8) id, (u8) reg); (u8) id, (u8) reg);
return res; return res;
} }
...@@ -875,7 +899,9 @@ static void emac_mdio_write(struct net_device *ndev, int id, int reg, int val) ...@@ -875,7 +899,9 @@ static void emac_mdio_write(struct net_device *ndev, int id, int reg, int val)
{ {
struct emac_instance *dev = netdev_priv(ndev); struct emac_instance *dev = netdev_priv(ndev);
__emac_mdio_write(dev->mdio_instance ? dev->mdio_instance : dev, __emac_mdio_write((dev->mdio_instance &&
dev->phy.gpcs_address != id) ?
dev->mdio_instance : dev,
(u8) id, (u8) reg, (u16) val); (u8) id, (u8) reg, (u16) val);
} }
...@@ -2367,7 +2393,11 @@ static int __devinit emac_init_phy(struct emac_instance *dev) ...@@ -2367,7 +2393,11 @@ static int __devinit emac_init_phy(struct emac_instance *dev)
* XXX I probably should move these settings to the dev tree * XXX I probably should move these settings to the dev tree
*/ */
dev->phy.address = -1; dev->phy.address = -1;
dev->phy.features = SUPPORTED_100baseT_Full | SUPPORTED_MII; dev->phy.features = SUPPORTED_MII;
if (emac_phy_supports_gige(dev->phy_mode))
dev->phy.features |= SUPPORTED_1000baseT_Full;
else
dev->phy.features |= SUPPORTED_100baseT_Full;
dev->phy.pause = 1; dev->phy.pause = 1;
return 0; return 0;
...@@ -2406,7 +2436,9 @@ static int __devinit emac_init_phy(struct emac_instance *dev) ...@@ -2406,7 +2436,9 @@ static int __devinit emac_init_phy(struct emac_instance *dev)
* Note that the busy_phy_map is currently global * Note that the busy_phy_map is currently global
* while it should probably be per-ASIC... * while it should probably be per-ASIC...
*/ */
dev->phy.address = dev->cell_index; dev->phy.gpcs_address = dev->gpcs_address;
if (dev->phy.gpcs_address == 0xffffffff)
dev->phy.address = dev->cell_index;
} }
emac_configure(dev); emac_configure(dev);
...@@ -2516,6 +2548,8 @@ static int __devinit emac_init_config(struct emac_instance *dev) ...@@ -2516,6 +2548,8 @@ static int __devinit emac_init_config(struct emac_instance *dev)
dev->phy_address = 0xffffffff; dev->phy_address = 0xffffffff;
if (emac_read_uint_prop(np, "phy-map", &dev->phy_map, 0)) if (emac_read_uint_prop(np, "phy-map", &dev->phy_map, 0))
dev->phy_map = 0xffffffff; dev->phy_map = 0xffffffff;
if (emac_read_uint_prop(np, "gpcs-address", &dev->gpcs_address, 0))
dev->gpcs_address = 0xffffffff;
if (emac_read_uint_prop(np->parent, "clock-frequency", &dev->opb_bus_freq, 1)) if (emac_read_uint_prop(np->parent, "clock-frequency", &dev->opb_bus_freq, 1))
return -ENXIO; return -ENXIO;
if (emac_read_uint_prop(np, "tah-device", &dev->tah_ph, 0)) if (emac_read_uint_prop(np, "tah-device", &dev->tah_ph, 0))
...@@ -2559,6 +2593,9 @@ static int __devinit emac_init_config(struct emac_instance *dev) ...@@ -2559,6 +2593,9 @@ static int __devinit emac_init_config(struct emac_instance *dev)
/* Check EMAC version */ /* Check EMAC version */
if (of_device_is_compatible(np, "ibm,emac4sync")) { if (of_device_is_compatible(np, "ibm,emac4sync")) {
dev->features |= (EMAC_FTR_EMAC4 | EMAC_FTR_EMAC4SYNC); dev->features |= (EMAC_FTR_EMAC4 | EMAC_FTR_EMAC4SYNC);
if (of_device_is_compatible(np, "ibm,emac-460ex") ||
of_device_is_compatible(np, "ibm,emac-460gt"))
dev->features |= EMAC_FTR_460EX_PHY_CLK_FIX;
} else if (of_device_is_compatible(np, "ibm,emac4")) { } else if (of_device_is_compatible(np, "ibm,emac4")) {
dev->features |= EMAC_FTR_EMAC4; dev->features |= EMAC_FTR_EMAC4;
if (of_device_is_compatible(np, "ibm,emac-440gx")) if (of_device_is_compatible(np, "ibm,emac-440gx"))
...@@ -2826,6 +2863,9 @@ static int __devinit emac_probe(struct of_device *ofdev, ...@@ -2826,6 +2863,9 @@ static int __devinit emac_probe(struct of_device *ofdev,
ndev->dev_addr[0], ndev->dev_addr[1], ndev->dev_addr[2], ndev->dev_addr[0], ndev->dev_addr[1], ndev->dev_addr[2],
ndev->dev_addr[3], ndev->dev_addr[4], ndev->dev_addr[5]); ndev->dev_addr[3], ndev->dev_addr[4], ndev->dev_addr[5]);
if (dev->phy_mode == PHY_MODE_SGMII)
printk(KERN_NOTICE "%s: in SGMII mode\n", ndev->name);
if (dev->phy.address >= 0) if (dev->phy.address >= 0)
printk("%s: found %s PHY (0x%02x)\n", ndev->name, printk("%s: found %s PHY (0x%02x)\n", ndev->name,
dev->phy.def->name, dev->phy.address); dev->phy.def->name, dev->phy.address);
......
...@@ -190,6 +190,9 @@ struct emac_instance { ...@@ -190,6 +190,9 @@ struct emac_instance {
struct delayed_work link_work; struct delayed_work link_work;
int link_polling; int link_polling;
/* GPCS PHY infos */
u32 gpcs_address;
/* Shared MDIO if any */ /* Shared MDIO if any */
u32 mdio_ph; u32 mdio_ph;
struct of_device *mdio_dev; struct of_device *mdio_dev;
...@@ -317,6 +320,10 @@ struct emac_instance { ...@@ -317,6 +320,10 @@ struct emac_instance {
* The 405EX and 460EX contain the EMAC4SYNC core * The 405EX and 460EX contain the EMAC4SYNC core
*/ */
#define EMAC_FTR_EMAC4SYNC 0x00000200 #define EMAC_FTR_EMAC4SYNC 0x00000200
/*
* Set if we need phy clock workaround for 460ex or 460gt
*/
#define EMAC_FTR_460EX_PHY_CLK_FIX 0x00000400
/* Right now, we don't quite handle the always/possible masks on the /* Right now, we don't quite handle the always/possible masks on the
...@@ -344,6 +351,7 @@ enum { ...@@ -344,6 +351,7 @@ enum {
#ifdef CONFIG_IBM_NEW_EMAC_NO_FLOW_CTRL #ifdef CONFIG_IBM_NEW_EMAC_NO_FLOW_CTRL
EMAC_FTR_NO_FLOW_CONTROL_40x | EMAC_FTR_NO_FLOW_CONTROL_40x |
#endif #endif
EMAC_FTR_460EX_PHY_CLK_FIX |
EMAC_FTR_440EP_PHY_CLK_FIX, EMAC_FTR_440EP_PHY_CLK_FIX,
}; };
......
...@@ -38,6 +38,16 @@ static inline void phy_write(struct mii_phy *phy, int reg, int val) ...@@ -38,6 +38,16 @@ static inline void phy_write(struct mii_phy *phy, int reg, int val)
phy->mdio_write(phy->dev, phy->address, reg, val); phy->mdio_write(phy->dev, phy->address, reg, val);
} }
static inline int gpcs_phy_read(struct mii_phy *phy, int reg)
{
return phy->mdio_read(phy->dev, phy->gpcs_address, reg);
}
static inline void gpcs_phy_write(struct mii_phy *phy, int reg, int val)
{
phy->mdio_write(phy->dev, phy->gpcs_address, reg, val);
}
int emac_mii_reset_phy(struct mii_phy *phy) int emac_mii_reset_phy(struct mii_phy *phy)
{ {
int val; int val;
...@@ -62,6 +72,37 @@ int emac_mii_reset_phy(struct mii_phy *phy) ...@@ -62,6 +72,37 @@ int emac_mii_reset_phy(struct mii_phy *phy)
return limit <= 0; return limit <= 0;
} }
int emac_mii_reset_gpcs(struct mii_phy *phy)
{
int val;
int limit = 10000;
val = gpcs_phy_read(phy, MII_BMCR);
val &= ~(BMCR_ISOLATE | BMCR_ANENABLE);
val |= BMCR_RESET;
gpcs_phy_write(phy, MII_BMCR, val);
udelay(300);
while (limit--) {
val = gpcs_phy_read(phy, MII_BMCR);
if (val >= 0 && (val & BMCR_RESET) == 0)
break;
udelay(10);
}
if ((val & BMCR_ISOLATE) && limit > 0)
gpcs_phy_write(phy, MII_BMCR, val & ~BMCR_ISOLATE);
if (limit > 0 && phy->mode == PHY_MODE_SGMII) {
/* Configure GPCS interface to recommended setting for SGMII */
gpcs_phy_write(phy, 0x04, 0x8120); /* AsymPause, FDX */
gpcs_phy_write(phy, 0x07, 0x2801); /* msg_pg, toggle */
gpcs_phy_write(phy, 0x00, 0x0140); /* 1Gbps, FDX */
}
return limit <= 0;
}
static int genmii_setup_aneg(struct mii_phy *phy, u32 advertise) static int genmii_setup_aneg(struct mii_phy *phy, u32 advertise)
{ {
int ctl, adv; int ctl, adv;
...@@ -332,6 +373,33 @@ static int m88e1111_init(struct mii_phy *phy) ...@@ -332,6 +373,33 @@ static int m88e1111_init(struct mii_phy *phy)
return 0; return 0;
} }
static int m88e1112_init(struct mii_phy *phy)
{
/*
* Marvell 88E1112 PHY needs to have the SGMII MAC
* interace (page 2) properly configured to
* communicate with the 460EX/GT GPCS interface.
*/
u16 reg_short;
pr_debug("%s: Marvell 88E1112 Ethernet\n", __func__);
/* Set access to Page 2 */
phy_write(phy, 0x16, 0x0002);
phy_write(phy, 0x00, 0x0040); /* 1Gbps */
reg_short = (u16)(phy_read(phy, 0x1a));
reg_short |= 0x8000; /* bypass Auto-Negotiation */
phy_write(phy, 0x1a, reg_short);
emac_mii_reset_phy(phy); /* reset MAC interface */
/* Reset access to Page 0 */
phy_write(phy, 0x16, 0x0000);
return 0;
}
static int et1011c_init(struct mii_phy *phy) static int et1011c_init(struct mii_phy *phy)
{ {
u16 reg_short; u16 reg_short;
...@@ -384,11 +452,27 @@ static struct mii_phy_def m88e1111_phy_def = { ...@@ -384,11 +452,27 @@ static struct mii_phy_def m88e1111_phy_def = {
.ops = &m88e1111_phy_ops, .ops = &m88e1111_phy_ops,
}; };
static struct mii_phy_ops m88e1112_phy_ops = {
.init = m88e1112_init,
.setup_aneg = genmii_setup_aneg,
.setup_forced = genmii_setup_forced,
.poll_link = genmii_poll_link,
.read_link = genmii_read_link
};
static struct mii_phy_def m88e1112_phy_def = {
.phy_id = 0x01410C90,
.phy_id_mask = 0x0ffffff0,
.name = "Marvell 88E1112 Ethernet",
.ops = &m88e1112_phy_ops,
};
static struct mii_phy_def *mii_phy_table[] = { static struct mii_phy_def *mii_phy_table[] = {
&et1011c_phy_def, &et1011c_phy_def,
&cis8201_phy_def, &cis8201_phy_def,
&bcm5248_phy_def, &bcm5248_phy_def,
&m88e1111_phy_def, &m88e1111_phy_def,
&m88e1112_phy_def,
&genmii_phy_def, &genmii_phy_def,
NULL NULL
}; };
......
...@@ -57,6 +57,7 @@ struct mii_phy { ...@@ -57,6 +57,7 @@ struct mii_phy {
or determined automaticaly */ or determined automaticaly */
int address; /* PHY address */ int address; /* PHY address */
int mode; /* PHY mode */ int mode; /* PHY mode */
int gpcs_address; /* GPCS PHY address */
/* 1: autoneg enabled, 0: disabled */ /* 1: autoneg enabled, 0: disabled */
int autoneg; int autoneg;
...@@ -81,5 +82,6 @@ struct mii_phy { ...@@ -81,5 +82,6 @@ struct mii_phy {
*/ */
int emac_mii_phy_probe(struct mii_phy *phy, int address); int emac_mii_phy_probe(struct mii_phy *phy, int address);
int emac_mii_reset_phy(struct mii_phy *phy); int emac_mii_reset_phy(struct mii_phy *phy);
int emac_mii_reset_gpcs(struct mii_phy *phy);
#endif /* __IBM_NEWEMAC_PHY_H */ #endif /* __IBM_NEWEMAC_PHY_H */
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