[PATCH RFC 08/10] net: stmmac:sti: Add STi SOC glue driver.

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 




From: Srinivas Kandagatla <srinivas.kandagatla@xxxxxx>

STi series SOCs have a glue layer on top of the synopsis gmac IP, this
glue layer needs to be configured before the gmac driver starts using
the IP.

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

Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@xxxxxx>
---
 .../devicetree/bindings/net/sti-dwmac.txt          |   45 +++
 drivers/net/ethernet/stmicro/stmmac/Makefile       |    1 +
 drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c    |  294 ++++++++++++++++++++
 3 files changed, 340 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/net/sti-dwmac.txt
 create mode 100644 drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c

diff --git a/Documentation/devicetree/bindings/net/sti-dwmac.txt b/Documentation/devicetree/bindings/net/sti-dwmac.txt
new file mode 100644
index 0000000..5431d9d
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/sti-dwmac.txt
@@ -0,0 +1,45 @@
+STMicroelectronics SoC DWMAC controller
+
+The device node has following properties.
+
+Required properties:
+ - compatible	: Can be "st,stih415-dwmac", "st,stih416-dwmac" or
+   "st,stid127-dwmac".
+ - reg		: Offset of the glue configuration register map in system
+   configuration regmap pointed by st,syscon property and size.
+ - st,syscon	: Should be phandle to system configuration node which
+   encompases this glue registers.
+ - st,tx-retime-src: This specifies which clk is wired up to the mac for
+   retimeing tx lines. This is totally board dependent and can take one of the
+   posssible values from "txclk", "clk_125", "phyclk" or "clkgen".
+
+Optional properties:
+ - resets	: phandle pointing to the system reset controller with correct
+   reset line index for ethernet reset.
+
+Sub-nodes:
+The dwmac core should be added as subnode to STMicroelectronics dwmac glue.
+- dwmac :	The binding details of dwmac can be found in
+  Documentation/devicetree/bindings/net/stmmac.txt
+
+Example:
+
+ethernet0: ethernet0{
+	#address-cells = <1>;
+	#size-cells = <1>;
+	compatible		= "st,stih415-dwmac";
+	reg			= <0x148 0x4>;
+	resets			= <&softreset STIH415_ETH0_SOFTRESET>;
+	st,syscon		= <&syscfg_rear>;
+	st,tx-retime-src	= "clk_125";
+	ranges;
+
+	dwmac0:dwmac@fe810000 {
+		device_type 	= "network";
+		compatible	= "snps,dwmac", "snps,dwmac-3.610";
+		reg 		= <0xfe810000 0x8000>;
+		interrupts 	= <0 147 0>;
+		interrupt-names = "macirq";
+		...
+	};
+};
diff --git a/drivers/net/ethernet/stmicro/stmmac/Makefile b/drivers/net/ethernet/stmicro/stmmac/Makefile
index 356a9dd..32db223 100644
--- a/drivers/net/ethernet/stmicro/stmmac/Makefile
+++ b/drivers/net/ethernet/stmicro/stmmac/Makefile
@@ -1,6 +1,7 @@
 obj-$(CONFIG_STMMAC_ETH) += stmmac.o
 stmmac-$(CONFIG_STMMAC_PLATFORM) += stmmac_platform.o
 stmmac-$(CONFIG_STMMAC_PCI) += stmmac_pci.o
+stmmac-$(CONFIG_ARCH_STI) += dwmac-sti.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-sti.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c
new file mode 100644
index 0000000..34cfa96
--- /dev/null
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c
@@ -0,0 +1,294 @@
+/**
+ * dwmac-sti.c - STMicroelectronics DWMAC Specific Glue layer
+ *
+ * Copyright (C) 2003-2013 STMicroelectronics (R&D) Limited
+ * Author: Srinivas Kandagatla <srinivas.kandagatla@xxxxxx>
+ *
+ * Inspired by drivers/usb/dwc3/dwc3-exynos.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/phy.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <linux/clk.h>
+#include <linux/of.h>
+#include <linux/of_net.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+
+#define BITS_MASK(lsb, msb)	((BIT(msb - lsb + 1) - 1) << lsb)
+
+#define TX_RETIME_SRC_MASK		BITS_MASK(6, 8)
+#define ETH_SEL_TX_RETIME_CLK		BIT(8)
+#define ETH_SEL_INTERNAL_NOTEXT_PHYCLK	BIT(7)
+#define ETH_SEL_TXCLK_NOT_CLK125	BIT(6)
+
+#define ENMII_MASK			BITS_MASK(5, 5)
+#define ENMII				BIT(5)
+
+/**
+ * 3 bits [4:2]
+ *	000-GMII/MII
+ *	001-RGMII
+ *	010-SGMII
+ *	100-RMII
+*/
+#define MII_PHY_SEL_MASK		BITS_MASK(2, 4)
+#define ETH_PHY_SEL_RMII		BIT(4)
+#define ETH_PHY_SEL_SGMII		BIT(3)
+#define ETH_PHY_SEL_RGMII		BIT(2)
+#define ETH_PHY_SEL_GMII		0x0
+#define ETH_PHY_SEL_MII			0x0
+
+struct sti_dwmac {
+	int	interface;
+	int	tx_retime_src;
+	int	reg;
+	struct	device *dev;
+	struct	regmap *regmap;
+	struct	device_node *dwmac_np;
+	struct	reset_control *rstc;
+};
+
+static u32 phy_intf_sels[] = {
+	[PHY_INTERFACE_MODE_MII]	= ETH_PHY_SEL_MII,
+	[PHY_INTERFACE_MODE_GMII]	= ETH_PHY_SEL_GMII,
+	[PHY_INTERFACE_MODE_RGMII]	= ETH_PHY_SEL_RGMII,
+	[PHY_INTERFACE_MODE_RGMII_ID]	= ETH_PHY_SEL_RGMII,
+	[PHY_INTERFACE_MODE_SGMII]	= ETH_PHY_SEL_SGMII,
+	[PHY_INTERFACE_MODE_RMII]	= ETH_PHY_SEL_RMII,
+};
+
+enum {
+	TX_RETIME_SRC_NA,
+	TX_RETIME_SRC_TXCLK,
+	TX_RETIME_SRC_MII_CLK_125,
+	TX_RETIME_SRC_PHYCLK,
+	TX_RETIME_SRC_CLKGEN,
+};
+
+static const char * const tx_retime_srcs[] = {
+	[TX_RETIME_SRC_NA]		= "",
+	[TX_RETIME_SRC_TXCLK]		= "txclk",
+	[TX_RETIME_SRC_MII_CLK_125]	= "clk_125",
+	[TX_RETIME_SRC_PHYCLK]		= "phyclk",
+	[TX_RETIME_SRC_CLKGEN]		= "clkgen",
+};
+
+/**
+ * TX lines are always retimed with a clk, which can vary depending
+ * on the board configuration. Below is the table of these bits
+ * in eth configuration register depending on source of retime clk.
+ *
+ *---------------------------------------------------------------
+ * src	 | tx_rt_clk	| int_not_ext_phyclk	| txclk_n_clk125|
+ *---------------------------------------------------------------
+ * txclk |	0	|	n/a		|	1	|
+ *---------------------------------------------------------------
+ * ck_125|	0	|	n/a		|	0	|
+ *---------------------------------------------------------------
+ * phyclk|	1	|	0		|	n/a	|
+ *---------------------------------------------------------------
+ * clkgen|	1	|	1		|	n/a	|
+ *---------------------------------------------------------------
+ */
+
+static u32 tx_retime_val[] = {
+	[TX_RETIME_SRC_TXCLK]		= ETH_SEL_TXCLK_NOT_CLK125,
+	[TX_RETIME_SRC_MII_CLK_125]	= 0x0,
+	[TX_RETIME_SRC_PHYCLK]		= ETH_SEL_TX_RETIME_CLK,
+	[TX_RETIME_SRC_CLKGEN]		= ETH_SEL_TX_RETIME_CLK |
+					 ETH_SEL_INTERNAL_NOTEXT_PHYCLK,
+};
+
+static int sti_get_tx_retime_src(struct device_node *np)
+{
+	const char *rs;
+	int err, i;
+
+	err = of_property_read_string(np, "st,tx-retime-src", &rs);
+	if (err < 0)
+		return err;
+
+	for (i = 0; i < ARRAY_SIZE(tx_retime_srcs); i++)
+		if (!strcasecmp(rs, tx_retime_srcs[i]))
+			return i;
+
+	return -EINVAL;
+}
+
+static int sti_dwmac_parse_data(struct sti_dwmac *dwmac, struct device *dev)
+{
+	struct resource res;
+	struct device_node *np	= dev->of_node;
+	struct device_node *stmmac_np;
+	struct regmap	*regmap;
+	int tx_retime_src;
+
+	stmmac_np = of_get_next_available_child(np, NULL);
+	if (!stmmac_np) {
+		dev_info(dev, "No dwmac node found\n");
+		return -EINVAL;
+	}
+
+	if (!of_device_is_compatible(stmmac_np, "snps,dwmac")) {
+		dev_info(dev, "dwmac node isn't compatible with snps,dwmac\n");
+		return -EINVAL;
+	}
+
+	dwmac->interface = of_get_phy_mode(stmmac_np);
+	of_node_put(stmmac_np);
+
+	if (of_address_to_resource(np, 0, &res))
+		return -EINVAL;
+
+	regmap = syscon_regmap_lookup_by_phandle(np, "st,syscon");
+	if (IS_ERR(regmap))
+		return PTR_ERR(regmap);
+
+	tx_retime_src = sti_get_tx_retime_src(np);
+	if (tx_retime_src <= 0)
+		return tx_retime_src;
+
+	dwmac->tx_retime_src = tx_retime_src;
+	dwmac->regmap = regmap;
+	dwmac->dwmac_np = stmmac_np;
+	dwmac->reg = res.start;
+	dwmac->dev = dev;
+	dwmac->rstc = reset_control_get(dev, NULL);
+
+	if (IS_ERR(dwmac->rstc))
+		dwmac->rstc = NULL;
+
+	return 0;
+}
+
+static int sti_dwmac_setup(struct sti_dwmac *dwmac)
+{
+	struct regmap	*regmap = dwmac->regmap;
+	int tx_retime_src	= dwmac->tx_retime_src;
+	int iface		= dwmac->interface;
+	int reg			= dwmac->reg;
+	u32 val;
+
+	regmap_update_bits(regmap, reg, MII_PHY_SEL_MASK,
+					phy_intf_sels[iface]);
+
+	val = (iface == PHY_INTERFACE_MODE_REVMII) ? 0 : ENMII;
+	regmap_update_bits(regmap, reg, ENMII_MASK, val);
+
+	regmap_update_bits(regmap, reg, TX_RETIME_SRC_MASK,
+				tx_retime_val[tx_retime_src]);
+
+	/* Enable the IP */
+	if (dwmac->rstc)
+		reset_control_deassert(dwmac->rstc);
+
+	return 0;
+}
+
+static int sti_dwmac_probe(struct platform_device *pdev)
+{
+	struct device		*dev = &pdev->dev;
+	struct device_node	*node = dev->of_node;
+	int			ret = -ENOMEM;
+	struct sti_dwmac	*dwmac;
+
+	dwmac = devm_kzalloc(dev, sizeof(*dwmac), GFP_KERNEL);
+
+	if (!dwmac)
+		return -ENOMEM;
+
+	ret = sti_dwmac_parse_data(dwmac, dev);
+	if (ret) {
+		dev_err(dev, "Unable to parse OF data\n");
+		return ret;
+	}
+
+	ret = sti_dwmac_setup(dwmac);
+	if (ret) {
+		dev_err(dev, "couldn't setup SoC glue (%d)\n", ret);
+		return ret;
+	}
+
+	if (node) {
+		ret = of_platform_populate(node, NULL, NULL, dev);
+		if (ret) {
+			dev_err(dev, "failed to add dwmac core\n");
+			return ret;
+		}
+	} else {
+		dev_err(dev, "no device node, failed to add dwmac core\n");
+		return -ENODEV;
+	}
+
+	platform_set_drvdata(pdev, dwmac);
+
+	return 0;
+}
+
+static int sti_dwmac_remove(struct platform_device *pdev)
+{
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int sti_dwmac_suspend(struct device *dev)
+{
+	struct sti_dwmac *dwmac = dev_get_drvdata(dev);
+
+	if (!device_child_may_wakeup(dev))
+		reset_control_assert(dwmac->rstc);
+
+	return 0;
+}
+
+static int sti_dwmac_resume(struct device *dev)
+{
+	struct sti_dwmac *dwmac = dev_get_drvdata(dev);
+
+	if (!device_child_may_wakeup(dev))
+		sti_dwmac_setup(dwmac);
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(sti_dwmac_pm_ops, sti_dwmac_suspend, sti_dwmac_resume);
+#endif
+
+static const struct of_device_id sti_dwmac_match[] = {
+	{ .compatible = "st,stih415-dwmac" },
+	{ .compatible = "st,stih416-dwmac" },
+	{ .compatible = "st,stid127-dwmac" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, sti_dwmac_match);
+
+static struct platform_driver sti_dwmac_driver = {
+	.probe		= sti_dwmac_probe,
+	.remove		= sti_dwmac_remove,
+	.driver		= {
+		.name	= "sti-dwmac",
+		.of_match_table = of_match_ptr(sti_dwmac_match),
+#ifdef CONFIG_PM
+		.pm     = &sti_dwmac_pm_ops,
+#endif
+	},
+};
+
+module_platform_driver(sti_dwmac_driver);
+
+MODULE_ALIAS("platform:sti-dwmac");
+MODULE_AUTHOR("Srinivas Kandagatla <srinivas.kandagatla@xxxxxx>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("STMicroelectronics DWMAC Glue Layer");
-- 
1.7.6.5

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Device Tree Compilter]     [Device Tree Spec]     [Linux Driver Backports]     [Video for Linux]     [Linux USB Devel]     [Linux PCI Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [XFree86]     [Yosemite Backpacking]
  Powered by Linux