b53: allow configuration through device tree
[openwrt/staging/rmilecki.git] / target / linux / generic / files / drivers / net / phy / b53 / b53_common.c
index b884010d28268f317ff7a2920562f2bb31a1104c..482966a909de43b74af2c30588c5ddda2e0ca8b6 100644 (file)
@@ -24,6 +24,8 @@
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/switch.h>
+#include <linux/of.h>
+#include <linux/of_net.h>
 #include <linux/platform_data/b53.h>
 
 #include "b53_regs.h"
@@ -478,35 +480,88 @@ static void b53_switch_reset_gpio(struct b53_device *dev)
        dev->current_page = 0xff;
 }
 
-static int b53_switch_reset(struct b53_device *dev)
+static int b53_configure_ports_of(struct b53_device *dev)
 {
-       u8 cpu_port = dev->sw_dev.cpu_port;
-       u8 mgmt;
+       struct device_node *dn, *pn;
+       u32 port_num;
 
-       b53_switch_reset_gpio(dev);
+       dn = of_get_child_by_name(dev_of_node(dev->dev), "ports");
 
-       if (is539x(dev)) {
-               b53_write8(dev, B53_CTRL_PAGE, B53_SOFTRESET, 0x83);
-               b53_write8(dev, B53_CTRL_PAGE, B53_SOFTRESET, 0x00);
-       }
+       for_each_available_child_of_node(dn, pn) {
+               struct device_node *fixed_link;
 
-       b53_read8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, &mgmt);
+               if (of_property_read_u32(pn, "reg", &port_num))
+                       continue;
 
-       if (!(mgmt & SM_SW_FWD_EN)) {
-               mgmt &= ~SM_SW_FWD_MODE;
-               mgmt |= SM_SW_FWD_EN;
+               if (port_num > B53_CPU_PORT)
+                       continue;
 
-               b53_write8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, mgmt);
-               b53_read8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, &mgmt);
+               fixed_link = of_get_child_by_name(pn, "fixed-link");
+               if (fixed_link) {
+                       u32 spd;
+                       u8 po = GMII_PO_LINK;
+                       int mode = of_get_phy_mode(pn);
+
+                       if (!of_property_read_u32(fixed_link, "speed", &spd)) {
+                               switch (spd) {
+                               case 10:
+                                       po |= GMII_PO_SPEED_10M;
+                                       break;
+                               case 100:
+                                       po |= GMII_PO_SPEED_100M;
+                                       break;
+                               case 2000:
+                                       if (is_imp_port(dev, port_num))
+                                               po |= PORT_OVERRIDE_SPEED_2000M;
+                                       else
+                                               po |= GMII_PO_SPEED_2000M;
+                                       /* fall through */
+                               case 1000:
+                                       po |= GMII_PO_SPEED_1000M;
+                                       break;
+                               }
+                       }
 
-               if (!(mgmt & SM_SW_FWD_EN)) {
-                       pr_err("Failed to enable switch!\n");
-                       return -EINVAL;
+                       if (of_property_read_bool(fixed_link, "full-duplex"))
+                               po |= PORT_OVERRIDE_FULL_DUPLEX;
+                       if (of_property_read_bool(fixed_link, "pause"))
+                               po |= GMII_PO_RX_FLOW;
+                       if (of_property_read_bool(fixed_link, "asym-pause"))
+                               po |= GMII_PO_TX_FLOW;
+
+                       if (is_imp_port(dev, port_num)) {
+                               po |= PORT_OVERRIDE_EN;
+
+                               if (is5325(dev) &&
+                                   mode == PHY_INTERFACE_MODE_REVMII)
+                                       po |= PORT_OVERRIDE_RV_MII_25;
+
+                               b53_write8(dev, B53_CTRL_PAGE,
+                                          B53_PORT_OVERRIDE_CTRL, po);
+
+                               if (is5325(dev) &&
+                                   mode == PHY_INTERFACE_MODE_REVMII) {
+                                       b53_read8(dev, B53_CTRL_PAGE,
+                                                 B53_PORT_OVERRIDE_CTRL, &po);
+                                       if (!(po & PORT_OVERRIDE_RV_MII_25))
+                                       pr_err("Failed to enable reverse MII mode\n");
+                                       return -EINVAL;
+                               }
+                       } else {
+                               po |= GMII_PO_EN;
+                               b53_write8(dev, B53_CTRL_PAGE,
+                                          B53_GMII_PORT_OVERRIDE_CTRL(port_num),
+                                          po);
+                       }
                }
        }
 
-       /* enable all ports */
-       b53_enable_ports(dev);
+       return 0;
+}
+
+static int b53_configure_ports(struct b53_device *dev)
+{
+       u8 cpu_port = dev->sw_dev.cpu_port;
 
        /* configure MII port if necessary */
        if (is5325(dev)) {
@@ -576,6 +631,47 @@ static int b53_switch_reset(struct b53_device *dev)
                }
        }
 
+       return 0;
+}
+
+static int b53_switch_reset(struct b53_device *dev)
+{
+       int ret = 0;
+       u8 mgmt;
+
+       b53_switch_reset_gpio(dev);
+
+       if (is539x(dev)) {
+               b53_write8(dev, B53_CTRL_PAGE, B53_SOFTRESET, 0x83);
+               b53_write8(dev, B53_CTRL_PAGE, B53_SOFTRESET, 0x00);
+       }
+
+       b53_read8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, &mgmt);
+
+       if (!(mgmt & SM_SW_FWD_EN)) {
+               mgmt &= ~SM_SW_FWD_MODE;
+               mgmt |= SM_SW_FWD_EN;
+
+               b53_write8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, mgmt);
+               b53_read8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, &mgmt);
+
+               if (!(mgmt & SM_SW_FWD_EN)) {
+                       pr_err("Failed to enable switch!\n");
+                       return -EINVAL;
+               }
+       }
+
+       /* enable all ports */
+       b53_enable_ports(dev);
+
+       if (dev->dev->of_node)
+               ret = b53_configure_ports_of(dev);
+       else
+               ret = b53_configure_ports(dev);
+
+       if (ret)
+               return ret;
+
        b53_enable_mib(dev);
 
        return b53_flush_arl(dev);
@@ -1320,6 +1416,43 @@ static const struct b53_chip_data b53_switch_chips[] = {
        },
 };
 
+static int b53_switch_init_of(struct b53_device *dev)
+{
+       struct device_node *dn, *pn;
+       const char *alias;
+       u32 port_num;
+       u16 ports = 0;
+
+       dn = of_get_child_by_name(dev_of_node(dev->dev), "ports");
+       if (!dn)
+               return -EINVAL;
+
+       for_each_available_child_of_node(dn, pn) {
+               const char *label;
+               int len;
+
+               if (of_property_read_u32(pn, "reg", &port_num))
+                       continue;
+
+               if (port_num > B53_CPU_PORT)
+                       continue;
+
+               ports |= BIT(port_num);
+
+               label = of_get_property(pn, "label", &len);
+               if (label && !strcmp(label, "cpu"))
+                       dev->sw_dev.cpu_port = port_num;
+       }
+
+       dev->enabled_ports = ports;
+
+       if (!of_property_read_string(dev_of_node(dev->dev), "lede,alias",
+                                                &alias))
+               dev->sw_dev.alias = devm_kstrdup(dev->dev, alias, GFP_KERNEL);
+
+       return 0;
+}
+
 static int b53_switch_init(struct b53_device *dev)
 {
        struct switch_dev *sw_dev = &dev->sw_dev;
@@ -1383,6 +1516,12 @@ static int b53_switch_init(struct b53_device *dev)
                        sw_dev->cpu_port = 5;
        }
 
+       if (dev_of_node(dev->dev)) {
+               ret = b53_switch_init_of(dev);
+               if (ret)
+                       return ret;
+       }
+
        dev->enabled_ports |= BIT(sw_dev->cpu_port);
        sw_dev->ports = fls(dev->enabled_ports);