net: stmmac: Add SOCFPGA glue driver
authorDinh Nguyen <dinguyen@altera.com>
Thu, 27 Mar 2014 03:45:10 +0000 (22:45 -0500)
committerDavid S. Miller <davem@davemloft.net>
Fri, 28 Mar 2014 19:06:32 +0000 (15:06 -0400)
Like the STi and sunxi series SOCs, Altera's SOCFPGA also needs a glue layer
on top of the Synopsys gmac IP.

This patch adds the platform driver for the glue layer which configures the IP
before the generic STMMAC driver takes over.

Signed-off-by: Dinh Nguyen <dinguyen@altera.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/stmicro/stmmac/Kconfig
drivers/net/ethernet/stmicro/stmmac/Makefile
drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c [new file with mode: 0644]
drivers/net/ethernet/stmicro/stmmac/stmmac.h
drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c

index f2d7c702c77f3853fc05c90753f1ae90773d8753..2d09c116cbc8c57b9ded844082dc2693d27a2402 100644 (file)
@@ -26,6 +26,16 @@ config STMMAC_PLATFORM
 
          If unsure, say N.
 
+config DWMAC_SOCFPGA
+       bool "SOCFPGA dwmac support"
+       depends on STMMAC_PLATFORM && MFD_SYSCON && (ARCH_SOCFPGA || COMPILE_TEST)
+       help
+         Support for ethernet controller on Altera SOCFPGA
+
+         This selects the Altera SOCFPGA SoC glue layer support
+         for the stmmac device driver. This driver is used for
+         arria5 and cyclone5 FPGA SoCs.
+
 config DWMAC_SUNXI
        bool "Allwinner GMAC support"
        depends on STMMAC_PLATFORM && ARCH_SUNXI
index dcef28775dadbeca184d8aca56358c389f1220e8..18695ebef7e43cb636e60640fbd52e4835e36ede 100644 (file)
@@ -3,6 +3,7 @@ stmmac-$(CONFIG_STMMAC_PLATFORM) += stmmac_platform.o
 stmmac-$(CONFIG_STMMAC_PCI) += stmmac_pci.o
 stmmac-$(CONFIG_DWMAC_SUNXI) += dwmac-sunxi.o
 stmmac-$(CONFIG_DWMAC_STI) += dwmac-sti.o
+stmmac-$(CONFIG_DWMAC_SOCFPGA) += dwmac-socfpga.o
 stmmac-objs:= stmmac_main.o stmmac_ethtool.o stmmac_mdio.o ring_mode.o \
              chain_mode.o dwmac_lib.o dwmac1000_core.o  dwmac1000_dma.o \
              dwmac100_core.o dwmac100_dma.o enh_desc.o  norm_desc.o \
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c
new file mode 100644 (file)
index 0000000..fd8a217
--- /dev/null
@@ -0,0 +1,130 @@
+/* Copyright Altera Corporation (C) 2014. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Adopted from dwmac-sti.c
+ */
+
+#include <linux/mfd/syscon.h>
+#include <linux/of.h>
+#include <linux/of_net.h>
+#include <linux/phy.h>
+#include <linux/regmap.h>
+#include <linux/stmmac.h>
+
+#define SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_GMII_MII 0x0
+#define SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_RGMII 0x1
+#define SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_RMII 0x2
+#define SYSMGR_EMACGRP_CTRL_PHYSEL_WIDTH 2
+#define SYSMGR_EMACGRP_CTRL_PHYSEL_MASK 0x00000003
+
+struct socfpga_dwmac {
+       int     interface;
+       u32     reg_offset;
+       u32     reg_shift;
+       struct  device *dev;
+       struct regmap *sys_mgr_base_addr;
+};
+
+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;
+
+       dwmac->interface = of_get_phy_mode(np);
+
+       sys_mgr_base_addr = syscon_regmap_lookup_by_phandle(np, "altr,sysmgr-syscon");
+       if (IS_ERR(sys_mgr_base_addr)) {
+               dev_info(dev, "No sysmgr-syscon node found\n");
+               return PTR_ERR(sys_mgr_base_addr);
+       }
+
+       ret = of_property_read_u32_index(np, "altr,sysmgr-syscon", 1, &reg_offset);
+       if (ret) {
+               dev_info(dev, "Could not read reg_offset from sysmgr-syscon!\n");
+               return -EINVAL;
+       }
+
+       ret = of_property_read_u32_index(np, "altr,sysmgr-syscon", 2, &reg_shift);
+       if (ret) {
+               dev_info(dev, "Could not read reg_shift from sysmgr-syscon!\n");
+               return -EINVAL;
+       }
+
+       dwmac->reg_offset = reg_offset;
+       dwmac->reg_shift = reg_shift;
+       dwmac->sys_mgr_base_addr = sys_mgr_base_addr;
+       dwmac->dev = dev;
+
+       return 0;
+}
+
+static int socfpga_dwmac_setup(struct socfpga_dwmac *dwmac)
+{
+       struct regmap *sys_mgr_base_addr = dwmac->sys_mgr_base_addr;
+       int phymode = dwmac->interface;
+       u32 reg_offset = dwmac->reg_offset;
+       u32 reg_shift = dwmac->reg_shift;
+       u32 ctrl, val;
+
+       switch (phymode) {
+       case PHY_INTERFACE_MODE_RGMII:
+               val = SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_RGMII;
+               break;
+       case PHY_INTERFACE_MODE_MII:
+       case PHY_INTERFACE_MODE_GMII:
+               val = SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_GMII_MII;
+               break;
+       default:
+               dev_err(dwmac->dev, "bad phy mode %d\n", phymode);
+               return -EINVAL;
+       }
+
+       regmap_read(sys_mgr_base_addr, reg_offset, &ctrl);
+       ctrl &= ~(SYSMGR_EMACGRP_CTRL_PHYSEL_MASK << reg_shift);
+       ctrl |= val << reg_shift;
+
+       regmap_write(sys_mgr_base_addr, reg_offset, ctrl);
+       return 0;
+}
+
+static void *socfpga_dwmac_probe(struct platform_device *pdev)
+{
+       struct device           *dev = &pdev->dev;
+       int                     ret;
+       struct socfpga_dwmac    *dwmac;
+
+       dwmac = devm_kzalloc(dev, sizeof(*dwmac), GFP_KERNEL);
+       if (!dwmac)
+               return ERR_PTR(-ENOMEM);
+
+       ret = socfpga_dwmac_parse_data(dwmac, dev);
+       if (ret) {
+               dev_err(dev, "Unable to parse OF data\n");
+               return ERR_PTR(ret);
+       }
+
+       ret = socfpga_dwmac_setup(dwmac);
+       if (ret) {
+               dev_err(dev, "couldn't setup SoC glue (%d)\n", ret);
+               return ERR_PTR(ret);
+       }
+
+       return dwmac;
+}
+
+const struct stmmac_of_data socfpga_gmac_data = {
+       .setup = socfpga_dwmac_probe,
+};
index f9e60d7918c4ac0b800a664ecae26e0228033f53..ca01035634a76fbc88414f6550849cfa1c772403 100644 (file)
@@ -136,6 +136,9 @@ extern const struct stmmac_of_data sun7i_gmac_data;
 #ifdef CONFIG_DWMAC_STI
 extern const struct stmmac_of_data sti_gmac_data;
 #endif
+#ifdef CONFIG_DWMAC_SOCFPGA
+extern const struct stmmac_of_data socfpga_gmac_data;
+#endif
 extern struct platform_driver stmmac_pltfr_driver;
 static inline int stmmac_register_platform(void)
 {
index 8fb32a80f1c1999a18234a2daec7323affd3917a..46aef5108bea47c7d6e49b891937d05ac2610c85 100644 (file)
@@ -37,6 +37,9 @@ static const struct of_device_id stmmac_dt_ids[] = {
        { .compatible = "st,stih415-dwmac", .data = &sti_gmac_data},
        { .compatible = "st,stih416-dwmac", .data = &sti_gmac_data},
        { .compatible = "st,stid127-dwmac", .data = &sti_gmac_data},
+#endif
+#ifdef CONFIG_DWMAC_SOCFPGA
+       { .compatible = "altr,socfpga-stmmac", .data = &socfpga_gmac_data },
 #endif
        /* SoC specific glue layers should come before generic bindings */
        { .compatible = "st,spear600-gmac"},