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