generic: rtl836x: add support for Green Feature
authorTobias Wolf <github-NTEO@vplace.de>
Wed, 25 Jan 2017 15:40:57 +0000 (16:40 +0100)
committerMathias Kresin <dev@kresin.me>
Fri, 27 Jan 2017 10:10:10 +0000 (11:10 +0100)
The GPL licensed source code of Belkin contains an ASIC based "Green
Feature". This change adds support for this Green Feature that can be
activated with an DTS option or swconfig.

Signed-off-by: Tobias Wolf <github-NTEO@vplace.de>
target/linux/generic/files/drivers/net/phy/rtl8366s.c

index b54b4b13b06ea47e06c5a7de752b350dd70e7e24..1c8a106b104ed21f9e1b1fbcce21f725eb2ff22f 100644 (file)
 /* Port Enable Control register */
 #define RTL8366S_PECR                          0x0001
 
+/* Green Ethernet Feature (based on GPL_BELKIN_F5D8235-4_v1000 v1.01.24) */
+#define RTL8366S_GREEN_ETHERNET_CTRL_REG       0x000a
+#define RTL8366S_GREEN_ETHERNET_CTRL_MASK      0x0018
+#define RTL8366S_GREEN_ETHERNET_TX_BIT         (1 << 3)
+#define RTL8366S_GREEN_ETHERNET_RX_BIT         (1 << 4)
+
 /* Switch Security Control registers */
 #define RTL8366S_SSCR0                         0x0002
 #define RTL8366S_SSCR1                         0x0003
 #define RTL8366S_PHY_NO_OFFSET                 9
 #define RTL8366S_PHY_NO_MASK                   (0x1f << 9)
 
+/* Green Ethernet Feature for PHY ports */
+#define RTL8366S_PHY_POWER_SAVING_CTRL_REG     12
+#define RTL8366S_PHY_POWER_SAVING_MASK         0x1000
+
 /* LED control registers */
 #define RTL8366S_LED_BLINKRATE_REG             0x0420
 #define RTL8366S_LED_BLINKRATE_BIT             0
@@ -250,68 +260,6 @@ static int rtl8366s_reset_chip(struct rtl8366_smi *smi)
        return 0;
 }
 
-static int rtl8366s_setup(struct rtl8366_smi *smi)
-{
-       struct rtl8366_platform_data *pdata;
-       int err;
-       unsigned i;
-#ifdef CONFIG_OF
-       struct device_node *np;
-       unsigned num_initvals;
-       const __be32 *paddr;
-#endif
-
-       pdata = smi->parent->platform_data;
-       if (pdata && pdata->num_initvals && pdata->initvals) {
-               dev_info(smi->parent, "applying initvals\n");
-               for (i = 0; i < pdata->num_initvals; i++)
-                       REG_WR(smi, pdata->initvals[i].reg,
-                              pdata->initvals[i].val);
-       }
-
-#ifdef CONFIG_OF
-       np = smi->parent->of_node;
-
-       paddr = of_get_property(np, "realtek,initvals", &num_initvals);
-       if (paddr) {
-               dev_info(smi->parent, "applying initvals from DTS\n");
-
-               if (num_initvals < (2 * sizeof(*paddr)))
-                       return -EINVAL;
-
-               num_initvals /= sizeof(*paddr);
-
-               for (i = 0; i < num_initvals - 1; i += 2) {
-                       u32 reg = be32_to_cpup(paddr + i);
-                       u32 val = be32_to_cpup(paddr + i + 1);
-
-                       REG_WR(smi, reg, val);
-               }
-       }
-#endif
-
-       /* set maximum packet length to 1536 bytes */
-       REG_RMW(smi, RTL8366S_SGCR, RTL8366S_SGCR_MAX_LENGTH_MASK,
-               RTL8366S_SGCR_MAX_LENGTH_1536);
-
-       /* enable learning for all ports */
-       REG_WR(smi, RTL8366S_SSCR0, 0);
-
-       /* enable auto ageing for all ports */
-       REG_WR(smi, RTL8366S_SSCR1, 0);
-
-       /*
-        * discard VLAN tagged packets if the port is not a member of
-        * the VLAN with which the packets is associated.
-        */
-       REG_WR(smi, RTL8366S_VLAN_MEMBERINGRESS_REG, RTL8366S_PORT_ALL);
-
-       /* don't drop packets whose DA has not been learned */
-       REG_RMW(smi, RTL8366S_SSCR2, RTL8366S_SSCR2_DROP_UNKNOWN_DA, 0);
-
-       return 0;
-}
-
 static int rtl8366s_read_phy_reg(struct rtl8366_smi *smi,
                                 u32 phy_no, u32 page, u32 addr, u32 *data)
 {
@@ -378,6 +326,126 @@ static int rtl8366s_write_phy_reg(struct rtl8366_smi *smi,
        return 0;
 }
 
+static int rtl8366s_set_green_port(struct rtl8366_smi *smi, int port, int enable)
+{
+       int err;
+       u32 phyData;
+
+       if (port >= RTL8366S_NUM_PORTS)
+               return -EINVAL;
+
+       err = rtl8366s_read_phy_reg(smi, port, 0, RTL8366S_PHY_POWER_SAVING_CTRL_REG, &phyData);
+       if (err)
+               return err;
+
+       if (enable)
+               phyData |= RTL8366S_PHY_POWER_SAVING_MASK;
+       else
+               phyData &= ~RTL8366S_PHY_POWER_SAVING_MASK;
+
+       err = rtl8366s_write_phy_reg(smi, port, 0, RTL8366S_PHY_POWER_SAVING_CTRL_REG, phyData);
+       if (err)
+               return err;
+
+       return 0;
+}
+
+static int rtl8366s_set_green(struct rtl8366_smi *smi, int enable)
+{
+       int err;
+       unsigned i;
+       u32 data = 0;
+
+       if (!enable) {
+               for (i = 0; i <= RTL8366S_PHY_NO_MAX; i++) {
+                       rtl8366s_set_green_port(smi, i, 0);
+               }
+       }
+
+       if (enable)
+               data = (RTL8366S_GREEN_ETHERNET_TX_BIT | RTL8366S_GREEN_ETHERNET_RX_BIT);
+
+       REG_RMW(smi, RTL8366S_GREEN_ETHERNET_CTRL_REG, RTL8366S_GREEN_ETHERNET_CTRL_MASK, data);
+
+       return 0;
+}
+
+static int rtl8366s_setup(struct rtl8366_smi *smi)
+{
+       struct rtl8366_platform_data *pdata;
+       int err;
+       unsigned i;
+#ifdef CONFIG_OF
+       struct device_node *np;
+       unsigned num_initvals;
+       const __be32 *paddr;
+#endif
+
+       pdata = smi->parent->platform_data;
+       if (pdata && pdata->num_initvals && pdata->initvals) {
+               dev_info(smi->parent, "applying initvals\n");
+               for (i = 0; i < pdata->num_initvals; i++)
+                       REG_WR(smi, pdata->initvals[i].reg,
+                              pdata->initvals[i].val);
+       }
+
+#ifdef CONFIG_OF
+       np = smi->parent->of_node;
+
+       paddr = of_get_property(np, "realtek,initvals", &num_initvals);
+       if (paddr) {
+               dev_info(smi->parent, "applying initvals from DTS\n");
+
+               if (num_initvals < (2 * sizeof(*paddr)))
+                       return -EINVAL;
+
+               num_initvals /= sizeof(*paddr);
+
+               for (i = 0; i < num_initvals - 1; i += 2) {
+                       u32 reg = be32_to_cpup(paddr + i);
+                       u32 val = be32_to_cpup(paddr + i + 1);
+
+                       REG_WR(smi, reg, val);
+               }
+       }
+
+       if (of_property_read_bool(np, "realtek,green-ethernet-features")) {
+               dev_info(smi->parent, "activating Green Ethernet features\n");
+
+               err = rtl8366s_set_green(smi, 1);
+               if (err)
+                       return err;
+
+               for (i = 0; i <= RTL8366S_PHY_NO_MAX; i++) {
+                       err = rtl8366s_set_green_port(smi, i, 1);
+                       if (err)
+                               return err;
+               }
+       }
+#endif
+
+       /* set maximum packet length to 1536 bytes */
+       REG_RMW(smi, RTL8366S_SGCR, RTL8366S_SGCR_MAX_LENGTH_MASK,
+               RTL8366S_SGCR_MAX_LENGTH_1536);
+
+       /* enable learning for all ports */
+       REG_WR(smi, RTL8366S_SSCR0, 0);
+
+       /* enable auto ageing for all ports */
+       REG_WR(smi, RTL8366S_SSCR1, 0);
+
+       /*
+        * discard VLAN tagged packets if the port is not a member of
+        * the VLAN with which the packets is associated.
+        */
+       REG_WR(smi, RTL8366S_VLAN_MEMBERINGRESS_REG, RTL8366S_PORT_ALL);
+
+       /* don't drop packets whose DA has not been learned */
+       REG_RMW(smi, RTL8366S_SSCR2, RTL8366S_SSCR2_DROP_UNKNOWN_DA, 0);
+
+       return 0;
+}
+
 static int rtl8366_get_mib_counter(struct rtl8366_smi *smi, int counter,
                                   int port, unsigned long long *val)
 {
@@ -759,6 +827,32 @@ static int rtl8366s_sw_set_learning_enable(struct switch_dev *dev,
        return 0;
 }
 
+static int rtl8366s_sw_get_green(struct switch_dev *dev,
+                             const struct switch_attr *attr,
+                             struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data;
+       int err;
+
+       err = rtl8366_smi_read_reg(smi, RTL8366S_GREEN_ETHERNET_CTRL_REG, &data);
+       if (err)
+               return err;
+
+       val->value.i = ((data & (RTL8366S_GREEN_ETHERNET_TX_BIT | RTL8366S_GREEN_ETHERNET_RX_BIT)) != 0) ? 1 : 0;
+
+       return 0;
+}
+
+static int rtl8366s_sw_set_green(struct switch_dev *dev,
+                                const struct switch_attr *attr,
+                                struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+
+       return rtl8366s_set_green(smi, val->value.i);
+}
+
 static int rtl8366s_sw_get_port_link(struct switch_dev *dev,
                                     int port,
                                     struct switch_port_link *link)
@@ -846,6 +940,34 @@ static int rtl8366s_sw_get_port_led(struct switch_dev *dev,
        return 0;
 }
 
+static int rtl8366s_sw_get_green_port(struct switch_dev *dev,
+                                     const struct switch_attr *attr,
+                                     struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       int err;
+       u32 phyData;
+
+       if (val->port_vlan >= RTL8366S_NUM_PORTS)
+               return -EINVAL;
+
+       err = rtl8366s_read_phy_reg(smi, val->port_vlan, 0, RTL8366S_PHY_POWER_SAVING_CTRL_REG, &phyData);
+       if (err)
+               return err;
+
+       val->value.i = ((phyData & RTL8366S_PHY_POWER_SAVING_MASK) != 0) ? 1 : 0;
+
+       return 0;
+}
+
+static int rtl8366s_sw_set_green_port(struct switch_dev *dev,
+                                     const struct switch_attr *attr,
+                                     struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       return rtl8366s_set_green_port(smi, val->port_vlan, val->value.i);
+}
+
 static int rtl8366s_sw_reset_port_mibs(struct switch_dev *dev,
                                       const struct switch_attr *attr,
                                       struct switch_val *val)
@@ -905,6 +1027,13 @@ static struct switch_attr rtl8366s_globals[] = {
                .set = rtl8366s_sw_set_max_length,
                .get = rtl8366s_sw_get_max_length,
                .max = 3,
+       }, {
+               .type = SWITCH_TYPE_INT,
+               .name = "green_mode",
+               .description = "Get/Set the router green feature",
+               .set = rtl8366s_sw_set_green,
+               .get = rtl8366s_sw_get_green,
+               .max = 1,
        },
 };
 
@@ -928,6 +1057,13 @@ static struct switch_attr rtl8366s_port[] = {
                .max = 15,
                .set = rtl8366s_sw_set_port_led,
                .get = rtl8366s_sw_get_port_led,
+       }, {
+               .type = SWITCH_TYPE_INT,
+               .name = "green_port",
+               .description = "Get/Set port green feature (0 - 1)",
+               .max = 1,
+               .set = rtl8366s_sw_set_green_port,
+               .get = rtl8366s_sw_get_green_port,
        },
 };