Support setting port speed/link advertisement for RTL8366.

This implements port speed control for RTL8366RB/RTL8366S.

Tested on RTL8366RB, works fine except for trying to force
speed:1000 with auto negotation disabled (in which case the link
will stay down in my tests, but I couldn't try forcing it on
both ends of the link yet).

Signed-off-by: Tobias Diedrich <ranma+open...@tdiedrich.de>


Index: target/linux/generic/patches-3.3/731-rtl8366-port-adv.patch
===================================================================
--- target/linux/generic/patches-3.3/731-rtl8366-port-adv.patch (revision 0)
+++ target/linux/generic/patches-3.3/731-rtl8366-port-adv.patch (working copy)
@@ -0,0 +1,170 @@
+Index: linux-3.3.8/drivers/net/phy/rtl8366_smi.c
+===================================================================
+--- linux-3.3.8.orig/drivers/net/phy/rtl8366_smi.c     2012-08-25 
21:29:13.091588065 +0200
++++ linux-3.3.8/drivers/net/phy/rtl8366_smi.c  2012-08-25 21:29:29.441610887 
+0200
+@@ -15,6 +15,7 @@
+ #include <linux/gpio.h>
+ #include <linux/spinlock.h>
+ #include <linux/skbuff.h>
++#include <linux/mii.h>
+ #include <linux/rtl8366.h>
+ 
+ #ifdef CONFIG_RTL8366_SMI_DEBUG_FS
+@@ -972,6 +973,116 @@
+ }
+ EXPORT_SYMBOL_GPL(rtl8366_sw_set_port_pvid);
+ 
++int rtl8366_sw_get_port_adv(struct switch_dev *dev, int port,
++                          struct switch_port_adv *adv)
++{
++      struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
++      u16 advertise, ctrl1000, bmcr;
++
++      if (!smi->ops->mii_read ||
++          port >= smi->num_ports)
++              return -EINVAL;
++
++      advertise = smi->ops->mii_read(smi->mii_bus, port, MII_ADVERTISE);
++      ctrl1000  = smi->ops->mii_read(smi->mii_bus, port, MII_CTRL1000);
++      bmcr      = smi->ops->mii_read(smi->mii_bus, port, MII_BMCR);
++
++      if (advertise == 0xffff ||
++          ctrl1000 == 0xffff ||
++          bmcr == 0xffff)
++              return -EINVAL;
++
++      if (bmcr & BMCR_ANENABLE)
++              adv->aneg = 1;
++
++      if (ctrl1000 & (ADVERTISE_1000HALF|ADVERTISE_1000FULL))
++              adv->speed = SWITCH_PORT_SPEED_1000;
++      else if (advertise & (ADVERTISE_100HALF|ADVERTISE_100FULL))
++              adv->speed = SWITCH_PORT_SPEED_100;
++      else if (advertise & (ADVERTISE_10HALF|ADVERTISE_10FULL))
++              adv->speed = SWITCH_PORT_SPEED_10;
++      else
++              adv->speed = SWITCH_PORT_SPEED_UNKNOWN;
++
++      /* ADVERTISE_100FULL is reused for gige. */
++      if (advertise & (ADVERTISE_10FULL|ADVERTISE_100FULL))
++              adv->duplex = 1;
++
++      if (advertise & ADVERTISE_PAUSE_CAP)
++              adv->flow = 1;
++
++      return 0;
++}
++EXPORT_SYMBOL_GPL(rtl8366_sw_get_port_adv);
++
++int rtl8366_sw_set_port_adv(struct switch_dev *dev, int port,
++                          struct switch_port_adv *adv)
++{
++      struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
++      int err;
++      u16 advertise, ctrl1000, bmcr;
++
++      if (!smi->ops->mii_read ||
++          !smi->ops->mii_write ||
++          port >= smi->num_ports)
++              return -EINVAL;
++
++      /*
++       * Power-on defaults:
++       * ADVERTISE = 0x0de1
++       * CTRL1000 = 0x0e00
++       * BMCR = 0x1140
++       */
++
++      switch (adv->speed) {
++      default:
++      case SWITCH_PORT_SPEED_DEFAULT:
++      case SWITCH_PORT_SPEED_1000:
++              ctrl1000 = ADVERTISE_1000HALF;
++              advertise = ADVERTISE_100HALF|ADVERTISE_10HALF;
++              bmcr = BMCR_SPEED1000;
++              break;
++      case SWITCH_PORT_SPEED_100:
++              ctrl1000 = 0;
++              advertise = ADVERTISE_100HALF|ADVERTISE_10HALF;
++              bmcr = BMCR_SPEED100;
++              break;
++      case SWITCH_PORT_SPEED_10:
++              ctrl1000 = 0;
++              advertise = ADVERTISE_10HALF;
++              bmcr = 0;
++              break;
++      }
++      if (adv->duplex) {
++              /* NOTE: this bitmanipulation assumes only xxxHALF bits have
++               * been set so far. */
++              ctrl1000 |= ctrl1000 << 1;
++              advertise |= advertise << 1;
++              bmcr |= BMCR_FULLDPLX;
++      }
++      if (adv->flow)
++              advertise |= ADVERTISE_PAUSE_CAP|ADVERTISE_PAUSE_ASYM;
++      advertise |= ADVERTISE_CSMA;
++
++      if (adv->aneg)
++              bmcr |= BMCR_ANRESTART | BMCR_ANENABLE;
++
++      err = smi->ops->mii_write(smi->mii_bus, port, MII_ADVERTISE, advertise);
++      if (err)
++              return err;
++
++      err = smi->ops->mii_write(smi->mii_bus, port, MII_CTRL1000, ctrl1000);
++      if (err)
++              return err;
++
++      err = smi->ops->mii_write(smi->mii_bus, port, MII_BMCR, bmcr);
++      if (err)
++              return err;
++
++      return 0;
++}
++EXPORT_SYMBOL_GPL(rtl8366_sw_set_port_adv);
++
+ int rtl8366_sw_get_port_mib(struct switch_dev *dev,
+                           const struct switch_attr *attr,
+                           struct switch_val *val)
+Index: linux-3.3.8/drivers/net/phy/rtl8366rb.c
+===================================================================
+--- linux-3.3.8.orig/drivers/net/phy/rtl8366rb.c       2012-08-25 
21:29:13.091588065 +0200
++++ linux-3.3.8/drivers/net/phy/rtl8366rb.c    2012-08-25 21:29:13.561588713 
+0200
+@@ -1060,6 +1060,8 @@
+       .set_vlan_ports = rtl8366_sw_set_vlan_ports,
+       .get_port_pvid = rtl8366_sw_get_port_pvid,
+       .set_port_pvid = rtl8366_sw_set_port_pvid,
++      .get_port_adv = rtl8366_sw_get_port_adv,
++      .set_port_adv = rtl8366_sw_set_port_adv,
+       .reset_switch = rtl8366_sw_reset_switch,
+       .get_port_link = rtl8366rb_sw_get_port_link,
+ };
+Index: linux-3.3.8/drivers/net/phy/rtl8366_smi.h
+===================================================================
+--- linux-3.3.8.orig/drivers/net/phy/rtl8366_smi.h     2012-08-25 
21:29:13.091588065 +0200
++++ linux-3.3.8/drivers/net/phy/rtl8366_smi.h  2012-08-25 21:29:13.561588713 
+0200
+@@ -124,6 +124,10 @@
+ int rtl8366_sw_reset_switch(struct switch_dev *dev);
+ int rtl8366_sw_get_port_pvid(struct switch_dev *dev, int port, int *val);
+ int rtl8366_sw_set_port_pvid(struct switch_dev *dev, int port, int val);
++int rtl8366_sw_get_port_adv(struct switch_dev *dev, int port,
++                          struct switch_port_adv *adv);
++int rtl8366_sw_set_port_adv(struct switch_dev *dev, int port,
++                          struct switch_port_adv *adv);
+ int rtl8366_sw_get_port_mib(struct switch_dev *dev,
+                           const struct switch_attr *attr,
+                           struct switch_val *val);
+Index: linux-3.3.8/drivers/net/phy/rtl8366s.c
+===================================================================
+--- linux-3.3.8.orig/drivers/net/phy/rtl8366s.c        2012-08-25 
21:29:13.091588065 +0200
++++ linux-3.3.8/drivers/net/phy/rtl8366s.c     2012-08-25 21:29:13.561588713 
+0200
+@@ -940,6 +940,8 @@
+       .set_vlan_ports = rtl8366_sw_set_vlan_ports,
+       .get_port_pvid = rtl8366_sw_get_port_pvid,
+       .set_port_pvid = rtl8366_sw_set_port_pvid,
++      .get_port_adv = rtl8366_sw_get_port_adv,
++      .set_port_adv = rtl8366_sw_set_port_adv,
+       .reset_switch = rtl8366_sw_reset_switch,
+       .get_port_link = rtl8366s_sw_get_port_link,
+ };
_______________________________________________
openwrt-devel mailing list
openwrt-devel@lists.openwrt.org
https://lists.openwrt.org/mailman/listinfo/openwrt-devel

Reply via email to