#define RTL8306_MAGIC 0x8306
+static LIST_HEAD(phydevs);
+
struct rtl_priv {
+ struct list_head list;
struct switch_dev dev;
int page;
int type;
int do_cpu;
struct mii_bus *bus;
char hwname[sizeof(RTL_NAME_UNKNOWN)];
+};
- /* temporary register saves for port operations */
- int tmp_speed;
- int tmp_nway;
- int tmp_duplex;
+struct rtl_phyregs {
+ int nway;
+ int speed;
+ int duplex;
};
#define to_rtl(_dev) container_of(_dev, struct rtl_priv, dev)
RTL_REG_PORT##id##_LINK, \
RTL_REG_PORT##id##_SPEED, \
RTL_REG_PORT##id##_NWAY, \
+ RTL_REG_PORT##id##_NRESTART, \
RTL_REG_PORT##id##_DUPLEX, \
RTL_REG_PORT##id##_RXEN, \
RTL_REG_PORT##id##_TXEN
#define REG_PORT_SETTING(port, phy) \
[RTL_REG_PORT##port##_SPEED] = { 0, phy, 0, 1, 13, 0 }, \
[RTL_REG_PORT##port##_NWAY] = { 0, phy, 0, 1, 12, 0 }, \
+ [RTL_REG_PORT##port##_NRESTART] = { 0, phy, 0, 1, 9, 0 }, \
[RTL_REG_PORT##port##_DUPLEX] = { 0, phy, 0, 1, 8, 0 }, \
[RTL_REG_PORT##port##_TXEN] = { 0, phy, 24, 1, 11, 0 }, \
[RTL_REG_PORT##port##_RXEN] = { 0, phy, 24, 1, 10, 0 }, \
}
static void
-rtl_phy_save(struct switch_dev *dev, int port)
+rtl_phy_save(struct switch_dev *dev, int port, struct rtl_phyregs *regs)
{
- struct rtl_priv *priv = to_rtl(dev);
-
- priv->tmp_nway = rtl_get(dev, RTL_PORT_REG(port, NWAY));
- priv->tmp_speed = rtl_get(dev, RTL_PORT_REG(port, SPEED));
- priv->tmp_duplex = rtl_get(dev, RTL_PORT_REG(port, DUPLEX));
+ regs->nway = rtl_get(dev, RTL_PORT_REG(port, NWAY));
+ regs->speed = rtl_get(dev, RTL_PORT_REG(port, SPEED));
+ regs->duplex = rtl_get(dev, RTL_PORT_REG(port, DUPLEX));
}
static void
-rtl_phy_restore(struct switch_dev *dev, int port)
+rtl_phy_restore(struct switch_dev *dev, int port, struct rtl_phyregs *regs)
{
- struct rtl_priv *priv = to_rtl(dev);
-
- rtl_set(dev, RTL_PORT_REG(port, NWAY), priv->tmp_nway);
- rtl_set(dev, RTL_PORT_REG(port, SPEED), priv->tmp_speed);
- rtl_set(dev, RTL_PORT_REG(port, DUPLEX), priv->tmp_duplex);
+ rtl_set(dev, RTL_PORT_REG(port, NWAY), regs->nway);
+ rtl_set(dev, RTL_PORT_REG(port, SPEED), regs->speed);
+ rtl_set(dev, RTL_PORT_REG(port, DUPLEX), regs->duplex);
}
static void
{
rtl_set(dev, RTL_PORT_REG(port, RXEN), enabled);
rtl_set(dev, RTL_PORT_REG(port, TXEN), enabled);
+
+ if ((port >= 5) || !enabled)
+ return;
+
+ /* restart autonegotiation if enabled */
+ rtl_set(dev, RTL_PORT_REG(port, NRESTART), 1);
}
static int
{
int i;
int trunk_en, trunk_psel;
+ struct rtl_phyregs port5;
- rtl_phy_save(dev, 5);
+ rtl_phy_save(dev, 5, &port5);
/* disable rx/tx from PHYs */
for (i = 0; i < RTL8306_NUM_PORTS - 1; i++) {
/* restore trunking settings */
rtl_set(dev, RTL_REG_EN_TRUNK, trunk_en);
rtl_set(dev, RTL_REG_TRUNK_PORTSEL, trunk_psel);
-
- rtl_phy_restore(dev, 5);
+ rtl_phy_restore(dev, 5, &port5);
return 0;
}
rtl_attr_set_int(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
{
int idx = attr->id + (val->port_vlan * attr->ofs);
+ struct rtl_phyregs port;
if (attr->id >= ARRAY_SIZE(rtl_regs))
return -EINVAL;
(rtl_regs[idx].reg == 22) &&
(rtl_regs[idx].page == 0)) {
- rtl_phy_save(dev, val->port_vlan);
+ rtl_phy_save(dev, val->port_vlan, &port);
rtl_set(dev, idx, val->value.i);
- rtl_phy_restore(dev, val->port_vlan);
+ rtl_phy_restore(dev, val->port_vlan, &port);
} else {
rtl_set(dev, idx, val->value.i);
}
rtl_set_vlan(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
{
struct rtl_priv *priv = to_rtl(dev);
+ struct rtl_phyregs port;
int en = val->value.i;
int i;
for (i = 0; i < RTL8306_NUM_PORTS; i++) {
if (i > 3)
- rtl_phy_save(dev, val->port_vlan);
+ rtl_phy_save(dev, val->port_vlan, &port);
rtl_set(dev, RTL_PORT_REG(i, NULL_VID_REPLACE), 1);
rtl_set(dev, RTL_PORT_REG(i, VID_INSERT), (en ? (i == dev->cpu_port ? 0 : 1) : 1));
rtl_set(dev, RTL_PORT_REG(i, TAG_INSERT), (en ? (i == dev->cpu_port ? 2 : 1) : 3));
if (i > 3)
- rtl_phy_restore(dev, val->port_vlan);
+ rtl_phy_restore(dev, val->port_vlan, &port);
}
rtl_set(dev, RTL_REG_VLAN_ENABLE, en);
unsigned int chipid, chipver, chiptype;
int err;
+ /* Only init the switch for the primary PHY */
+ if (pdev->addr != 0)
+ return 0;
+
val.value.i = 1;
memcpy(&priv->dev, &rtldev, sizeof(struct switch_dev));
priv->do_cpu = 0;
struct rtl_priv priv;
u16 chipid;
+ /* Attach to primary LAN port and WAN port */
+ if (pdev->addr != 0 && pdev->addr != 4)
+ return 0;
+
priv.page = -1;
priv.bus = pdev->bus;
chipid = rtl_get(&priv.dev, RTL_REG_CHIPID);
{
struct rtl_priv *priv;
+ list_for_each_entry(priv, &phydevs, list) {
+ /*
+ * share one rtl_priv instance between virtual phy
+ * devices on the same bus
+ */
+ if (priv->bus == pdev->bus)
+ goto found;
+ }
priv = kzalloc(sizeof(struct rtl_priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
+ priv->bus = pdev->bus;
+
+found:
pdev->priv = priv;
return 0;
}
static int
rtl8306_config_aneg(struct phy_device *pdev)
{
+ struct rtl_priv *priv = pdev->priv;
+
+ /* Only for WAN */
+ if (pdev->addr == 0)
+ return 0;
+
+ /* Restart autonegotiation */
+ rtl_set(&priv->dev, RTL_PORT_REG(4, NWAY), 1);
+ rtl_set(&priv->dev, RTL_PORT_REG(4, NRESTART), 1);
+
return 0;
}
static int
rtl8306_read_status(struct phy_device *pdev)
{
- pdev->speed = SPEED_100;
- pdev->duplex = DUPLEX_FULL;
- pdev->state = PHY_UP;
+ struct rtl_priv *priv = pdev->priv;
+ struct switch_dev *dev = &priv->dev;
+
+ if (pdev->addr == 4) {
+ /* WAN */
+ pdev->speed = rtl_get(dev, RTL_PORT_REG(4, SPEED)) ? SPEED_100 : SPEED_10;
+ pdev->duplex = rtl_get(dev, RTL_PORT_REG(4, DUPLEX)) ? DUPLEX_FULL : DUPLEX_HALF;
+ pdev->link = !!rtl_get(dev, RTL_PORT_REG(4, LINK));
+ } else {
+ /* LAN */
+ pdev->speed = SPEED_100;
+ pdev->duplex = DUPLEX_FULL;
+ pdev->link = 1;
+ }
+
+ /*
+ * Bypass generic PHY status read,
+ * it doesn't work with this switch
+ */
+ if (pdev->link) {
+ pdev->state = PHY_RUNNING;
+ netif_carrier_on(pdev->attached_dev);
+ pdev->adjust_link(pdev->attached_dev);
+ } else {
+ pdev->state = PHY_NOLINK;
+ netif_carrier_off(pdev->attached_dev);
+ pdev->adjust_link(pdev->attached_dev);
+ }
return 0;
}
static struct phy_driver rtl8306_driver = {
.name = "Realtek RTL8306S",
+ .flags = PHY_HAS_MAGICANEG,
.phy_id = RTL8306_MAGIC,
.phy_id_mask = 0xffffffff,
.features = PHY_BASIC_FEATURES,