You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
220 lines
5.7 KiB
220 lines
5.7 KiB
#include <linux/phy.h>
|
|
#include "huanglong_phy.h"
|
|
#include "phy.h"
|
|
#include "hleth.h"
|
|
|
|
/*----------------------------Macro definition-------------------------------*/
|
|
#define NO_EEE 0
|
|
#define MAC_EEE 1
|
|
#define PHY_EEE 2
|
|
#define PARTNER_EEE 2
|
|
|
|
#define debug(fmt...)
|
|
struct phy_info {
|
|
char *name;
|
|
int phy_id;
|
|
char eee_available;/* eee support by this phy */
|
|
int (*eee_init)(struct phy_device *phy_dev);
|
|
};
|
|
|
|
/* GMAC register definition */
|
|
#define EEE_ENABLE 0x488
|
|
#define BIT_EEE_ENABLE (1 << 0)
|
|
#define EEE_TIMER 0x48C
|
|
#define EEE_LINK_STATUS 0x490
|
|
#define BIT_PHY_LINK_STATUS (1 << 0)
|
|
#define EEE_TIME_CLK_CNT 0x494
|
|
|
|
/* ----------------------------phy register-------------------------------*/
|
|
/* MMD: MDIO Manageable Device */
|
|
#define MACR 0x0D
|
|
#define MAADR 0x0E
|
|
#define EEE_DEV 0x3
|
|
#define EEE_CAPABILITY 0x14
|
|
#define EEELPAR_DEV 0x7
|
|
#define EEELPAR 0x3D /* EEE link partner ability register */
|
|
#define EEE_ADVERTISE 0x3c
|
|
#define LP_1000BASE_EEE (1 << 2)
|
|
#define LP_100BASE_EEE (1 << 1)
|
|
|
|
static struct phy_info phy_info_table[];
|
|
|
|
static struct phy_info *phy_search_ids(int phy_id)
|
|
{
|
|
int i;
|
|
struct phy_info *fit_info = NULL;
|
|
|
|
for (i = 0; phy_info_table[i].name != NULL; i++) {
|
|
if (phy_id == phy_info_table[i].phy_id)
|
|
fit_info = &phy_info_table[i];
|
|
}
|
|
|
|
return fit_info;
|
|
}
|
|
|
|
static inline int phy_mmd_read(struct phy_device *phy_dev,
|
|
u32 mmd_device, u32 regnum)
|
|
{
|
|
phy_write(phy_dev, MACR, mmd_device);/* function = 00 address */
|
|
phy_write(phy_dev, MAADR, regnum);
|
|
phy_write(phy_dev, MACR, 0x4000 | mmd_device);/* function = 01 data */
|
|
|
|
return phy_read(phy_dev, MAADR);
|
|
}
|
|
|
|
static inline int phy_mmd_write(struct phy_device *phy_dev,
|
|
u32 mmd_device, u32 regnum, u16 val)
|
|
{
|
|
phy_write(phy_dev, MACR, mmd_device);/* function = 00 address */
|
|
phy_write(phy_dev, MAADR, regnum);
|
|
phy_write(phy_dev, MACR, 0x4000 | mmd_device);/* function = 01 data */
|
|
|
|
return phy_write(phy_dev, MAADR, val);
|
|
}
|
|
|
|
static int smsc_lan8740_init(struct phy_device *phy_dev)
|
|
{
|
|
static int first_time;
|
|
int v, eee_type = 0;
|
|
/* Realtek LAN 8740 start to enable eee */
|
|
int eee_lan;
|
|
|
|
if (!first_time) {
|
|
eee_lan = phy_read(phy_dev, 0x10);
|
|
eee_lan |= 0x4;
|
|
phy_write(phy_dev, 0x10, eee_lan);
|
|
eee_lan = phy_read(phy_dev, 0x10);
|
|
debug("eee enable bit[45?] :%x\n", eee_lan);
|
|
/* auto negotiate after enable eee */
|
|
eee_lan = phy_read(phy_dev, 0x0);
|
|
eee_lan |= 0x200;
|
|
phy_write(phy_dev, 0x0, eee_lan);
|
|
first_time = 1;
|
|
}
|
|
|
|
v = phy_mmd_read(phy_dev, EEELPAR_DEV, EEELPAR);
|
|
debug("EEELPAR = 0x%x\n", v);
|
|
|
|
if (v & LP_100BASE_EEE)
|
|
eee_type |= HLETH_P_MAC_PORTSET_SPD_100M;
|
|
|
|
return eee_type;
|
|
}
|
|
|
|
static int rtl8211eg_init(struct phy_device *phy_dev)
|
|
{
|
|
int eee_type = 0, v;
|
|
|
|
v = phy_mmd_read(phy_dev, EEELPAR_DEV, EEELPAR);
|
|
debug("EEELPAR = 0x%x\n", v);
|
|
|
|
if (v & LP_100BASE_EEE)
|
|
eee_type |= HLETH_P_MAC_PORTSET_SPD_100M;
|
|
|
|
return eee_type;
|
|
}
|
|
|
|
static int festa_eee_init(struct phy_device *phy_dev)
|
|
{
|
|
static int first_time_init;
|
|
int v, eee_type = 0;
|
|
|
|
if (!first_time_init) {
|
|
/* EEE_CAPABILITY register: support 100M-BaseT */
|
|
v = phy_mmd_read(phy_dev, EEE_DEV, EEE_CAPABILITY);
|
|
phy_mmd_write(phy_dev, EEE_DEV, EEE_CAPABILITY, v|(1<<1));
|
|
|
|
/* EEE_ADVERTISEMENT register: advertising 100M-BaseT */
|
|
v = phy_mmd_read(phy_dev, EEELPAR_DEV, EEE_ADVERTISE);
|
|
phy_mmd_write(phy_dev, EEELPAR_DEV, EEE_ADVERTISE, v|(1<<1));
|
|
|
|
v = phy_read(phy_dev, MII_BMCR);
|
|
v |= (BMCR_ANENABLE | BMCR_ANRESTART);
|
|
phy_write(phy_dev, MII_BMCR, v);/* auto-neg restart */
|
|
|
|
first_time_init = 1;
|
|
}
|
|
|
|
v = phy_mmd_read(phy_dev, EEELPAR_DEV, EEELPAR);
|
|
debug("EEELPAR = 0x%x\n", v);
|
|
|
|
if (v & LP_100BASE_EEE)
|
|
eee_type |= HLETH_P_MAC_PORTSET_SPD_100M;
|
|
|
|
return eee_type;
|
|
}
|
|
|
|
static struct phy_info phy_info_table[] = {
|
|
/* phy_name phy_id eee_available phy_driver */
|
|
/* SMSC */
|
|
{"SMSC LAN8740", 0x0007c110, MAC_EEE, &smsc_lan8740_init},
|
|
/* Realtek */
|
|
{"Realtek 8211EG", 0x001cc915, PHY_EEE, &rtl8211eg_init},
|
|
{"Festa V220", HUANGLONG_PHY_ID_FESTAV220, MAC_EEE, &festa_eee_init},
|
|
{"Festa V212", HUANGLONG_PHY_ID_FESTAV212, MAC_EEE, &festa_eee_init},
|
|
{0, 0, 0, 0},
|
|
};
|
|
|
|
void hleth_autoeee_init(struct hleth_netdev_priv *priv, int link_stat)
|
|
{
|
|
int phy_id = priv->phy->phy_id;
|
|
int eee_available, lp_eee_capable, v;
|
|
struct phy_info *phy_info = NULL;
|
|
|
|
if (priv->eee_init != NULL)
|
|
goto eee_init;
|
|
|
|
phy_info = phy_search_ids(phy_id);
|
|
if (phy_info != NULL) {
|
|
eee_available = phy_info->eee_available;
|
|
debug("fit phy_id:0x%x, phy_name:%s, eee:%d\n",
|
|
phy_info->phy_id, phy_info->name, eee_available);
|
|
|
|
if (!eee_available)
|
|
goto not_support;
|
|
|
|
if (eee_available == PHY_EEE) {
|
|
debug("enter phy-EEE mode\n");
|
|
v = readl(priv->port_base + EEE_ENABLE);
|
|
v &= ~BIT_EEE_ENABLE;/* disable auto-EEE */
|
|
writel(v, priv->port_base + EEE_ENABLE);
|
|
return;
|
|
}
|
|
|
|
priv->eee_init = phy_info->eee_init;
|
|
eee_init:
|
|
lp_eee_capable = priv->eee_init(priv->phy);
|
|
if (link_stat & HLETH_P_MAC_PORTSET_LINKED) {
|
|
if (lp_eee_capable & link_stat) {
|
|
/* EEE_1us: 0x7c for 125M */
|
|
writel(0x7c, priv->port_base + EEE_TIME_CLK_CNT);
|
|
writel(0x4002710, priv->port_base + EEE_TIMER);
|
|
|
|
v = readl(priv->port_base + EEE_LINK_STATUS);
|
|
v |= 0x3 << 1;/* auto EEE and ... */
|
|
v |= BIT_PHY_LINK_STATUS;/* phy linkup */
|
|
writel(v, priv->port_base + EEE_LINK_STATUS);
|
|
|
|
v = readl(priv->port_base + EEE_ENABLE);
|
|
v |= BIT_EEE_ENABLE;/* enable EEE */
|
|
writel(v, priv->port_base + EEE_ENABLE);
|
|
|
|
debug("enter auto-EEE mode\n");
|
|
} else {
|
|
debug("link partner not support EEE\n");
|
|
};
|
|
return;
|
|
} else {
|
|
v = readl(priv->port_base + EEE_LINK_STATUS);
|
|
v &= ~(BIT_PHY_LINK_STATUS);/* phy linkdown */
|
|
writel(v, priv->port_base + EEE_LINK_STATUS);
|
|
return;
|
|
}
|
|
}
|
|
|
|
not_support:
|
|
priv->eee_init = NULL;
|
|
debug("non-EEE mode\n");
|
|
}
|
|
|