net: stmmac: add fix_mac_speed support for socfpga
authorLey Foon Tan <lftan@altera.com>
Wed, 20 Aug 2014 06:33:33 +0000 (14:33 +0800)
committerDavid S. Miller <davem@davemloft.net>
Fri, 22 Aug 2014 19:33:48 +0000 (12:33 -0700)
This patch adds fix_mac_speed() support for
Altera socfpga Ethernet controller. Emac splitter is a
soft IP core in FPGA system that converts GMII interface from
Synopsys mac to RGMII/SGMII interface. This splitter core is
an optional IP if user would like to use RGMII/SGMII
interface in their system. Software needs to update a register
in splitter core when there is speed change.

Signed-off-by: Ley Foon Tan <lftan@altera.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Documentation/devicetree/bindings/net/socfpga-dwmac.txt
drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c

index 2a60cd3e8d5ddb7bdf3b2caad2bc414a3d8566e0..3a9d6795160654080ec4aaa114304b62d5f0754f 100644 (file)
@@ -12,6 +12,10 @@ Required properties:
  - altr,sysmgr-syscon : Should be the phandle to the system manager node that
    encompasses the glue register, the register offset, and the register shift.
 
+Optional properties:
+altr,emac-splitter: Should be the phandle to the emac splitter soft IP node if
+               DWMAC controller is connected emac splitter.
+
 Example:
 
 gmac0: ethernet@ff700000 {
index ec632e666c56ad5138384cae4f23e43b45ad4d34..cd613d711108a989374a8ba1473e1aa7e6c75317 100644 (file)
@@ -17,6 +17,7 @@
 
 #include <linux/mfd/syscon.h>
 #include <linux/of.h>
+#include <linux/of_address.h>
 #include <linux/of_net.h>
 #include <linux/phy.h>
 #include <linux/regmap.h>
 #define SYSMGR_EMACGRP_CTRL_PHYSEL_WIDTH 2
 #define SYSMGR_EMACGRP_CTRL_PHYSEL_MASK 0x00000003
 
+#define EMAC_SPLITTER_CTRL_REG                 0x0
+#define EMAC_SPLITTER_CTRL_SPEED_MASK          0x3
+#define EMAC_SPLITTER_CTRL_SPEED_10            0x2
+#define EMAC_SPLITTER_CTRL_SPEED_100           0x3
+#define EMAC_SPLITTER_CTRL_SPEED_1000          0x0
+
 struct socfpga_dwmac {
        int     interface;
        u32     reg_offset;
@@ -37,14 +44,46 @@ struct socfpga_dwmac {
        struct  device *dev;
        struct regmap *sys_mgr_base_addr;
        struct reset_control *stmmac_rst;
+       void __iomem *splitter_base;
 };
 
+static void socfpga_dwmac_fix_mac_speed(void *priv, unsigned int speed)
+{
+       struct socfpga_dwmac *dwmac = (struct socfpga_dwmac *)priv;
+       void __iomem *splitter_base = dwmac->splitter_base;
+       u32 val;
+
+       if (!splitter_base)
+               return;
+
+       val = readl(splitter_base + EMAC_SPLITTER_CTRL_REG);
+       val &= ~EMAC_SPLITTER_CTRL_SPEED_MASK;
+
+       switch (speed) {
+       case 1000:
+               val |= EMAC_SPLITTER_CTRL_SPEED_1000;
+               break;
+       case 100:
+               val |= EMAC_SPLITTER_CTRL_SPEED_100;
+               break;
+       case 10:
+               val |= EMAC_SPLITTER_CTRL_SPEED_10;
+               break;
+       default:
+               return;
+       }
+
+       writel(val, splitter_base + EMAC_SPLITTER_CTRL_REG);
+}
+
 static int socfpga_dwmac_parse_data(struct socfpga_dwmac *dwmac, struct device *dev)
 {
        struct device_node *np = dev->of_node;
        struct regmap *sys_mgr_base_addr;
        u32 reg_offset, reg_shift;
        int ret;
+       struct device_node *np_splitter;
+       struct resource res_splitter;
 
        dwmac->stmmac_rst = devm_reset_control_get(dev,
                                                  STMMAC_RESOURCE_NAME);
@@ -73,6 +112,21 @@ static int socfpga_dwmac_parse_data(struct socfpga_dwmac *dwmac, struct device *
                return -EINVAL;
        }
 
+       np_splitter = of_parse_phandle(np, "altr,emac-splitter", 0);
+       if (np_splitter) {
+               if (of_address_to_resource(np_splitter, 0, &res_splitter)) {
+                       dev_info(dev, "Missing emac splitter address\n");
+                       return -EINVAL;
+               }
+
+               dwmac->splitter_base = (void *)devm_ioremap_resource(dev,
+                       &res_splitter);
+               if (!dwmac->splitter_base) {
+                       dev_info(dev, "Failed to mapping emac splitter\n");
+                       return -EINVAL;
+               }
+       }
+
        dwmac->reg_offset = reg_offset;
        dwmac->reg_shift = reg_shift;
        dwmac->sys_mgr_base_addr = sys_mgr_base_addr;
@@ -91,6 +145,7 @@ static int socfpga_dwmac_setup(struct socfpga_dwmac *dwmac)
 
        switch (phymode) {
        case PHY_INTERFACE_MODE_RGMII:
+       case PHY_INTERFACE_MODE_RGMII_ID:
                val = SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_RGMII;
                break;
        case PHY_INTERFACE_MODE_MII:
@@ -102,6 +157,13 @@ static int socfpga_dwmac_setup(struct socfpga_dwmac *dwmac)
                return -EINVAL;
        }
 
+       /* Overwrite val to GMII if splitter core is enabled. The phymode here
+        * is the actual phy mode on phy hardware, but phy interface from
+        * EMAC core is GMII.
+        */
+       if (dwmac->splitter_base)
+               val = SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_GMII_MII;
+
        regmap_read(sys_mgr_base_addr, reg_offset, &ctrl);
        ctrl &= ~(SYSMGR_EMACGRP_CTRL_PHYSEL_MASK << reg_shift);
        ctrl |= val << reg_shift;
@@ -196,4 +258,5 @@ const struct stmmac_of_data socfpga_gmac_data = {
        .setup = socfpga_dwmac_probe,
        .init = socfpga_dwmac_init,
        .exit = socfpga_dwmac_exit,
+       .fix_mac_speed = socfpga_dwmac_fix_mac_speed,
 };