All of lore.kernel.org
 help / color / mirror / Atom feed
From: Chen-Yu Tsai <wens@csie.org>
To: Maxime Ripard <maxime.ripard@free-electrons.com>,
	Florian Fainelli <f.fainelli@gmail.com>,
	Rob Herring <robh+dt@kernel.org>, Pawel Moll <pawel.moll@arm.com>,
	Mark Rutland <mark.rutland@arm.com>,
	Ian Campbell <ijc+devicetree@hellion.org.uk>,
	Kumar Gala <galak@codeaurora.org>
Cc: Chen-Yu Tsai <wens@csie.org>,
	netdev@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
	linux-kernel@vger.kernel.org, devicetree@vger.kernel.org,
	LABBE Corentin <clabbe.montjoie@gmail.com>
Subject: [PATCH RFC 2/5] net: phy: sun8i-h3-ephy: Add driver for Allwinner H3 Ethernet PHY
Date: Tue,  5 Apr 2016 00:22:31 +0800	[thread overview]
Message-ID: <1459786954-12649-3-git-send-email-wens@csie.org> (raw)
In-Reply-To: <1459786954-12649-1-git-send-email-wens@csie.org>

The Allwinner H3 SoC incorporates an Ethernet PHY. This is enabled and
configured through a memory mapped hardware register.

This same register also configures the MAC interface mode and TX clock
source. Also covered by the register, but not supported in this driver,
are TX/RX clock delay chains and inverters, and an RMII module.

Signed-off-by: Chen-Yu Tsai <wens@csie.org>
---
 drivers/net/phy/Kconfig         |   8 ++
 drivers/net/phy/Makefile        |   1 +
 drivers/net/phy/sun8i-h3-ephy.c | 266 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 275 insertions(+)
 create mode 100644 drivers/net/phy/sun8i-h3-ephy.c

diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
index 6dad9a9c356c..f4b9eb6e9114 100644
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -276,3 +276,11 @@ endif # PHYLIB
 config MICREL_KS8995MA
 	tristate "Micrel KS8995MA 5-ports 10/100 managed Ethernet switch"
 	depends on SPI
+
+config SUN8I_H3_EPHY
+	tristate "Allwinner sun8i H3 Ethernet PHY"
+	depends on OF && HAS_IOMEM && COMMON_CLK && RESET_CONTROLLER
+	depends on MACH_SUN8I || COMPILE_TEST
+	help
+	  This modules provides a driver for the internal Ethernet PHY
+	  and Ethernet MAC controls found in the Allwinner H3 SoC.
diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
index fcdbb9299fab..7cec3aceb518 100644
--- a/drivers/net/phy/Makefile
+++ b/drivers/net/phy/Makefile
@@ -44,3 +44,4 @@ obj-$(CONFIG_MDIO_MOXART)	+= mdio-moxart.o
 obj-$(CONFIG_MDIO_BCM_UNIMAC)	+= mdio-bcm-unimac.o
 obj-$(CONFIG_MICROCHIP_PHY)	+= microchip.o
 obj-$(CONFIG_MDIO_BCM_IPROC)	+= mdio-bcm-iproc.o
+obj-$(CONFIG_SUN8I_H3_EPHY)	+= sun8i-h3-ephy.o
diff --git a/drivers/net/phy/sun8i-h3-ephy.c b/drivers/net/phy/sun8i-h3-ephy.c
new file mode 100644
index 000000000000..bf2703bee024
--- /dev/null
+++ b/drivers/net/phy/sun8i-h3-ephy.c
@@ -0,0 +1,266 @@
+/*
+ * Allwinner sun8i H3 E(thernet) PHY driver
+ *
+ * Copyright (C) 2016 Chen-Yu Tsai <wens@csie.org>
+ *
+ * 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.
+ *
+ * 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.
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+#include <linux/spinlock.h>
+
+#define REG_PHY_ADDR_SHIFT	20
+#define REG_PHY_ADDR_MASK	GENMASK(4, 0)
+#define REG_LED_POL		BIT(17)	/* 1: active low, 0: active high */
+#define REG_SHUTDOWN		BIT(16)	/* 1: shutdown, 0: power up */
+#define REG_PHY_SELECT		BIT(15) /* 1: internal PHY, 0: external PHY */
+
+#define REG_DEFAULT_VALUE	0x58000
+#define REG_DEFAULT_MASK	GENMASK(31, 15)
+
+#define REG_GPIT		2
+#define REG_ETCS_MASK		0x3
+#define SUN8I_H3_EMAC_PARENTS	2
+#define MII_PHY_CLK_RATE	25000000
+#define INT_TX_CLK_RATE		125000000
+#define MII_PHY_CLK_NAME	"mii_phy_tx"
+#define INT_TX_CLK_NAME		"int_tx"
+
+struct sun8i_h3_ephy {
+	struct device *dev;
+	void __iomem *reg;
+	struct clk *clk;
+	struct reset_control *reset;
+	struct clk *mac_clks[SUN8I_H3_EMAC_PARENTS + 1];
+};
+
+static DEFINE_SPINLOCK(sun8i_h3_ephy_lock);
+
+/* The EMAC clock/interface controls are much like those found on the A20,
+ * which is supported by sun7i-a20-gmac-clk. The H3 adds an RMII module,
+ * enabled by bit 13. Unfortunately vendor code and documentation don't
+ * explain  how it should work with the other bits, and no hardware
+ * actually uses it.
+ */
+static u32 sun8i_h3_emac_mux_table[SUN8I_H3_EMAC_PARENTS] = {
+	0x0,	/* Select TX clock from MII PHY */
+	0x2,	/* Select internal TX clock (for RGMII) */
+};
+
+static const char *sun8i_h3_emac_mux_parents[SUN8I_H3_EMAC_PARENTS] = {
+	MII_PHY_CLK_NAME,
+	INT_TX_CLK_NAME,
+};
+
+int sun8i_h3_ephy_register_clocks(struct sun8i_h3_ephy *phy)
+{
+	struct device *dev = phy->dev;
+	struct device_node *np = dev->of_node;
+	const char *clk_name;
+	struct clk_mux *mux;
+	struct clk_gate *gate;
+	int ret;
+
+	if (of_property_read_string(np, "clock-output-names", &clk_name)) {
+		dev_err(phy->dev, "No clock output name given\n");
+		return -EINVAL;
+	}
+
+	/* allocate mux and gate clock structs */
+	mux = devm_kzalloc(dev, sizeof(struct clk_mux), GFP_KERNEL);
+	if (!mux)
+		return -ENOMEM;
+
+	gate = devm_kzalloc(dev, sizeof(struct clk_gate), GFP_KERNEL);
+	if (!gate)
+		return -ENOMEM;
+
+	/* Register fixed clocks to use as parents */
+	phy->mac_clks[0] = clk_register_fixed_rate(dev, MII_PHY_CLK_NAME,
+						   NULL, 0, MII_PHY_CLK_RATE);
+	if (IS_ERR(phy->mac_clks[0]))
+		return PTR_ERR(phy->mac_clks[0]);
+
+	phy->mac_clks[1] = clk_register_fixed_rate(dev, INT_TX_CLK_NAME,
+						   NULL, 0, INT_TX_CLK_RATE);
+	if (IS_ERR(phy->mac_clks[1])) {
+		ret = PTR_ERR(phy->mac_clks[1]);
+		goto err_int_tx_clk;
+	}
+
+	/* set up gate and mux properties */
+	gate->reg = phy->reg;
+	gate->bit_idx = REG_GPIT;
+	gate->lock = &sun8i_h3_ephy_lock;
+	mux->reg = phy->reg;
+	mux->mask = REG_ETCS_MASK;
+	mux->table = sun8i_h3_emac_mux_table;
+	mux->lock = &sun8i_h3_ephy_lock;
+
+	phy->mac_clks[2] = clk_register_composite(dev, clk_name,
+						  sun8i_h3_emac_mux_parents,
+						  SUN8I_H3_EMAC_PARENTS,
+						  &mux->hw, &clk_mux_ops,
+						  NULL, NULL,
+						  &gate->hw, &clk_gate_ops,
+						  0);
+	if (IS_ERR(phy->mac_clks[2])) {
+		ret = PTR_ERR(phy->mac_clks[2]);
+		goto err_tx_clk;
+	}
+
+	ret = of_clk_add_provider(dev->of_node, of_clk_src_simple_get,
+				  phy->mac_clks[2]);
+	if (ret)
+		goto err_of_clk;
+
+	return 0;
+
+err_of_clk:
+	/* TODO: switch to clk_unregister_composite when it's available */
+	clk_unregister(phy->mac_clks[2]);
+err_tx_clk:
+	clk_unregister_fixed_rate(phy->mac_clks[1]);
+err_int_tx_clk:
+	clk_unregister_fixed_rate(phy->mac_clks[0]);
+
+	return ret;
+}
+
+static void sun8i_h3_ephy_init_phy(struct sun8i_h3_ephy *phy, u32 addr)
+{
+	u32 val = readl(phy->reg);
+
+	/* set default values, but don't touch clock settings */
+	val = (val & ~REG_DEFAULT_MASK) | REG_DEFAULT_VALUE;
+
+	if (addr < REG_PHY_ADDR_MASK) {
+		dev_info(phy->dev, "Using interal PHY at 0x%x\n", addr);
+
+		val |= addr << REG_PHY_ADDR_SHIFT;
+		val &= ~REG_SHUTDOWN;
+		val |= REG_PHY_SELECT;
+
+		if (of_property_read_bool(phy->dev->of_node,
+					  "allwinner,leds-active-low"))
+			val |= REG_LED_POL;
+	} else {
+		dev_info(phy->dev, "Using external PHY\n");
+	}
+
+	writel(val, phy->reg);
+}
+
+static void sun8i_h3_ephy_reset_phy(struct sun8i_h3_ephy *phy)
+{
+	u32 val;
+
+	val = readl(phy->reg);
+	val = (val & ~REG_DEFAULT_MASK) | REG_DEFAULT_VALUE;
+	writel(val, phy->reg);
+}
+
+int sun8i_h3_ephy_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *np = dev->of_node;
+	struct sun8i_h3_ephy *phy;
+	struct resource *res;
+	u32 addr = REG_PHY_ADDR_MASK;
+	int ret;
+
+	ret = of_property_read_u32(np, "allwinner,ephy-addr", &addr);
+	if (!ret && addr > REG_PHY_ADDR_MASK) {
+		dev_err(dev, "invalid PHY address: 0x%x\n", addr);
+		return -EINVAL;
+	} else if (ret && ret != -EINVAL) {
+		dev_err(dev, "could not get PHY address: %d\n", ret);
+		return ret;
+	}
+
+	phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
+	if (!phy)
+		return -ENOMEM;
+
+	phy->dev = dev;
+	platform_set_drvdata(pdev, phy);
+
+	phy->clk = devm_clk_get(dev, NULL);
+	if (IS_ERR(phy->clk)) {
+		dev_err(dev, "failed to get clock\n");
+		return PTR_ERR(phy->clk);
+	}
+
+	phy->reset = devm_reset_control_get(dev, NULL);
+	if (IS_ERR(phy->reset)) {
+		dev_err(dev, "failed to get reset control\n");
+		return PTR_ERR(phy->reset);
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	phy->reg = devm_ioremap_resource(dev, res);
+	if (IS_ERR(phy->reg)) {
+		dev_err(dev, "failed to map registers\n");
+		return PTR_ERR(phy->reg);
+	}
+
+	ret = clk_prepare_enable(phy->clk);
+	if (ret) {
+		dev_err(dev, "failed to enable clock\n");
+		return ret;
+	}
+
+	ret = reset_control_deassert(phy->reset);
+	if (ret) {
+		dev_err(dev, "failed to deassert reset control\n");
+		goto err_reset;
+	}
+
+	sun8i_h3_ephy_init_phy(phy, addr);
+
+	ret = sun8i_h3_ephy_register_clocks(phy);
+	if (ret)
+		goto err_tx_clk;
+
+	return 0;
+
+err_tx_clk:
+	sun8i_h3_ephy_reset_phy(phy);
+	reset_control_assert(phy->reset);
+err_reset:
+	clk_disable_unprepare(phy->clk);
+	return ret;
+}
+
+static const struct of_device_id sun8i_h3_ephy_of_match[] = {
+	{ .compatible = "allwinner,sun8i-h3-ephy", },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, sun8i_h3_ephy_of_match);
+
+static struct platform_driver sun8i_h3_ephy_driver = {
+	.probe	= sun8i_h3_ephy_probe,
+	.driver = {
+		.of_match_table	= sun8i_h3_ephy_of_match,
+		.name  = "sun8i-h3-ephy",
+	}
+};
+module_platform_driver(sun8i_h3_ephy_driver);
+
+MODULE_DESCRIPTION("Allwinner sun8i H3 Ethernet PHY driver");
+MODULE_AUTHOR("Chen-Yu Tsai <wens@csie.org>");
+MODULE_LICENSE("GPL v2");
-- 
2.7.0

WARNING: multiple messages have this Message-ID (diff)
From: wens@csie.org (Chen-Yu Tsai)
To: linux-arm-kernel@lists.infradead.org
Subject: [PATCH RFC 2/5] net: phy: sun8i-h3-ephy: Add driver for Allwinner H3 Ethernet PHY
Date: Tue,  5 Apr 2016 00:22:31 +0800	[thread overview]
Message-ID: <1459786954-12649-3-git-send-email-wens@csie.org> (raw)
In-Reply-To: <1459786954-12649-1-git-send-email-wens@csie.org>

The Allwinner H3 SoC incorporates an Ethernet PHY. This is enabled and
configured through a memory mapped hardware register.

This same register also configures the MAC interface mode and TX clock
source. Also covered by the register, but not supported in this driver,
are TX/RX clock delay chains and inverters, and an RMII module.

Signed-off-by: Chen-Yu Tsai <wens@csie.org>
---
 drivers/net/phy/Kconfig         |   8 ++
 drivers/net/phy/Makefile        |   1 +
 drivers/net/phy/sun8i-h3-ephy.c | 266 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 275 insertions(+)
 create mode 100644 drivers/net/phy/sun8i-h3-ephy.c

diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
index 6dad9a9c356c..f4b9eb6e9114 100644
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -276,3 +276,11 @@ endif # PHYLIB
 config MICREL_KS8995MA
 	tristate "Micrel KS8995MA 5-ports 10/100 managed Ethernet switch"
 	depends on SPI
+
+config SUN8I_H3_EPHY
+	tristate "Allwinner sun8i H3 Ethernet PHY"
+	depends on OF && HAS_IOMEM && COMMON_CLK && RESET_CONTROLLER
+	depends on MACH_SUN8I || COMPILE_TEST
+	help
+	  This modules provides a driver for the internal Ethernet PHY
+	  and Ethernet MAC controls found in the Allwinner H3 SoC.
diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
index fcdbb9299fab..7cec3aceb518 100644
--- a/drivers/net/phy/Makefile
+++ b/drivers/net/phy/Makefile
@@ -44,3 +44,4 @@ obj-$(CONFIG_MDIO_MOXART)	+= mdio-moxart.o
 obj-$(CONFIG_MDIO_BCM_UNIMAC)	+= mdio-bcm-unimac.o
 obj-$(CONFIG_MICROCHIP_PHY)	+= microchip.o
 obj-$(CONFIG_MDIO_BCM_IPROC)	+= mdio-bcm-iproc.o
+obj-$(CONFIG_SUN8I_H3_EPHY)	+= sun8i-h3-ephy.o
diff --git a/drivers/net/phy/sun8i-h3-ephy.c b/drivers/net/phy/sun8i-h3-ephy.c
new file mode 100644
index 000000000000..bf2703bee024
--- /dev/null
+++ b/drivers/net/phy/sun8i-h3-ephy.c
@@ -0,0 +1,266 @@
+/*
+ * Allwinner sun8i H3 E(thernet) PHY driver
+ *
+ * Copyright (C) 2016 Chen-Yu Tsai <wens@csie.org>
+ *
+ * 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.
+ *
+ * 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.
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+#include <linux/spinlock.h>
+
+#define REG_PHY_ADDR_SHIFT	20
+#define REG_PHY_ADDR_MASK	GENMASK(4, 0)
+#define REG_LED_POL		BIT(17)	/* 1: active low, 0: active high */
+#define REG_SHUTDOWN		BIT(16)	/* 1: shutdown, 0: power up */
+#define REG_PHY_SELECT		BIT(15) /* 1: internal PHY, 0: external PHY */
+
+#define REG_DEFAULT_VALUE	0x58000
+#define REG_DEFAULT_MASK	GENMASK(31, 15)
+
+#define REG_GPIT		2
+#define REG_ETCS_MASK		0x3
+#define SUN8I_H3_EMAC_PARENTS	2
+#define MII_PHY_CLK_RATE	25000000
+#define INT_TX_CLK_RATE		125000000
+#define MII_PHY_CLK_NAME	"mii_phy_tx"
+#define INT_TX_CLK_NAME		"int_tx"
+
+struct sun8i_h3_ephy {
+	struct device *dev;
+	void __iomem *reg;
+	struct clk *clk;
+	struct reset_control *reset;
+	struct clk *mac_clks[SUN8I_H3_EMAC_PARENTS + 1];
+};
+
+static DEFINE_SPINLOCK(sun8i_h3_ephy_lock);
+
+/* The EMAC clock/interface controls are much like those found on the A20,
+ * which is supported by sun7i-a20-gmac-clk. The H3 adds an RMII module,
+ * enabled by bit 13. Unfortunately vendor code and documentation don't
+ * explain  how it should work with the other bits, and no hardware
+ * actually uses it.
+ */
+static u32 sun8i_h3_emac_mux_table[SUN8I_H3_EMAC_PARENTS] = {
+	0x0,	/* Select TX clock from MII PHY */
+	0x2,	/* Select internal TX clock (for RGMII) */
+};
+
+static const char *sun8i_h3_emac_mux_parents[SUN8I_H3_EMAC_PARENTS] = {
+	MII_PHY_CLK_NAME,
+	INT_TX_CLK_NAME,
+};
+
+int sun8i_h3_ephy_register_clocks(struct sun8i_h3_ephy *phy)
+{
+	struct device *dev = phy->dev;
+	struct device_node *np = dev->of_node;
+	const char *clk_name;
+	struct clk_mux *mux;
+	struct clk_gate *gate;
+	int ret;
+
+	if (of_property_read_string(np, "clock-output-names", &clk_name)) {
+		dev_err(phy->dev, "No clock output name given\n");
+		return -EINVAL;
+	}
+
+	/* allocate mux and gate clock structs */
+	mux = devm_kzalloc(dev, sizeof(struct clk_mux), GFP_KERNEL);
+	if (!mux)
+		return -ENOMEM;
+
+	gate = devm_kzalloc(dev, sizeof(struct clk_gate), GFP_KERNEL);
+	if (!gate)
+		return -ENOMEM;
+
+	/* Register fixed clocks to use as parents */
+	phy->mac_clks[0] = clk_register_fixed_rate(dev, MII_PHY_CLK_NAME,
+						   NULL, 0, MII_PHY_CLK_RATE);
+	if (IS_ERR(phy->mac_clks[0]))
+		return PTR_ERR(phy->mac_clks[0]);
+
+	phy->mac_clks[1] = clk_register_fixed_rate(dev, INT_TX_CLK_NAME,
+						   NULL, 0, INT_TX_CLK_RATE);
+	if (IS_ERR(phy->mac_clks[1])) {
+		ret = PTR_ERR(phy->mac_clks[1]);
+		goto err_int_tx_clk;
+	}
+
+	/* set up gate and mux properties */
+	gate->reg = phy->reg;
+	gate->bit_idx = REG_GPIT;
+	gate->lock = &sun8i_h3_ephy_lock;
+	mux->reg = phy->reg;
+	mux->mask = REG_ETCS_MASK;
+	mux->table = sun8i_h3_emac_mux_table;
+	mux->lock = &sun8i_h3_ephy_lock;
+
+	phy->mac_clks[2] = clk_register_composite(dev, clk_name,
+						  sun8i_h3_emac_mux_parents,
+						  SUN8I_H3_EMAC_PARENTS,
+						  &mux->hw, &clk_mux_ops,
+						  NULL, NULL,
+						  &gate->hw, &clk_gate_ops,
+						  0);
+	if (IS_ERR(phy->mac_clks[2])) {
+		ret = PTR_ERR(phy->mac_clks[2]);
+		goto err_tx_clk;
+	}
+
+	ret = of_clk_add_provider(dev->of_node, of_clk_src_simple_get,
+				  phy->mac_clks[2]);
+	if (ret)
+		goto err_of_clk;
+
+	return 0;
+
+err_of_clk:
+	/* TODO: switch to clk_unregister_composite when it's available */
+	clk_unregister(phy->mac_clks[2]);
+err_tx_clk:
+	clk_unregister_fixed_rate(phy->mac_clks[1]);
+err_int_tx_clk:
+	clk_unregister_fixed_rate(phy->mac_clks[0]);
+
+	return ret;
+}
+
+static void sun8i_h3_ephy_init_phy(struct sun8i_h3_ephy *phy, u32 addr)
+{
+	u32 val = readl(phy->reg);
+
+	/* set default values, but don't touch clock settings */
+	val = (val & ~REG_DEFAULT_MASK) | REG_DEFAULT_VALUE;
+
+	if (addr < REG_PHY_ADDR_MASK) {
+		dev_info(phy->dev, "Using interal PHY at 0x%x\n", addr);
+
+		val |= addr << REG_PHY_ADDR_SHIFT;
+		val &= ~REG_SHUTDOWN;
+		val |= REG_PHY_SELECT;
+
+		if (of_property_read_bool(phy->dev->of_node,
+					  "allwinner,leds-active-low"))
+			val |= REG_LED_POL;
+	} else {
+		dev_info(phy->dev, "Using external PHY\n");
+	}
+
+	writel(val, phy->reg);
+}
+
+static void sun8i_h3_ephy_reset_phy(struct sun8i_h3_ephy *phy)
+{
+	u32 val;
+
+	val = readl(phy->reg);
+	val = (val & ~REG_DEFAULT_MASK) | REG_DEFAULT_VALUE;
+	writel(val, phy->reg);
+}
+
+int sun8i_h3_ephy_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *np = dev->of_node;
+	struct sun8i_h3_ephy *phy;
+	struct resource *res;
+	u32 addr = REG_PHY_ADDR_MASK;
+	int ret;
+
+	ret = of_property_read_u32(np, "allwinner,ephy-addr", &addr);
+	if (!ret && addr > REG_PHY_ADDR_MASK) {
+		dev_err(dev, "invalid PHY address: 0x%x\n", addr);
+		return -EINVAL;
+	} else if (ret && ret != -EINVAL) {
+		dev_err(dev, "could not get PHY address: %d\n", ret);
+		return ret;
+	}
+
+	phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
+	if (!phy)
+		return -ENOMEM;
+
+	phy->dev = dev;
+	platform_set_drvdata(pdev, phy);
+
+	phy->clk = devm_clk_get(dev, NULL);
+	if (IS_ERR(phy->clk)) {
+		dev_err(dev, "failed to get clock\n");
+		return PTR_ERR(phy->clk);
+	}
+
+	phy->reset = devm_reset_control_get(dev, NULL);
+	if (IS_ERR(phy->reset)) {
+		dev_err(dev, "failed to get reset control\n");
+		return PTR_ERR(phy->reset);
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	phy->reg = devm_ioremap_resource(dev, res);
+	if (IS_ERR(phy->reg)) {
+		dev_err(dev, "failed to map registers\n");
+		return PTR_ERR(phy->reg);
+	}
+
+	ret = clk_prepare_enable(phy->clk);
+	if (ret) {
+		dev_err(dev, "failed to enable clock\n");
+		return ret;
+	}
+
+	ret = reset_control_deassert(phy->reset);
+	if (ret) {
+		dev_err(dev, "failed to deassert reset control\n");
+		goto err_reset;
+	}
+
+	sun8i_h3_ephy_init_phy(phy, addr);
+
+	ret = sun8i_h3_ephy_register_clocks(phy);
+	if (ret)
+		goto err_tx_clk;
+
+	return 0;
+
+err_tx_clk:
+	sun8i_h3_ephy_reset_phy(phy);
+	reset_control_assert(phy->reset);
+err_reset:
+	clk_disable_unprepare(phy->clk);
+	return ret;
+}
+
+static const struct of_device_id sun8i_h3_ephy_of_match[] = {
+	{ .compatible = "allwinner,sun8i-h3-ephy", },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, sun8i_h3_ephy_of_match);
+
+static struct platform_driver sun8i_h3_ephy_driver = {
+	.probe	= sun8i_h3_ephy_probe,
+	.driver = {
+		.of_match_table	= sun8i_h3_ephy_of_match,
+		.name  = "sun8i-h3-ephy",
+	}
+};
+module_platform_driver(sun8i_h3_ephy_driver);
+
+MODULE_DESCRIPTION("Allwinner sun8i H3 Ethernet PHY driver");
+MODULE_AUTHOR("Chen-Yu Tsai <wens@csie.org>");
+MODULE_LICENSE("GPL v2");
-- 
2.7.0

  parent reply	other threads:[~2016-04-04 16:22 UTC|newest]

Thread overview: 35+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-04-04 16:22 [PATCH RFC 0/5] net: phy: sun8i-h3-ephy: Add Allwinner H3 Ethernet PHY driver Chen-Yu Tsai
2016-04-04 16:22 ` Chen-Yu Tsai
2016-04-04 16:22 ` Chen-Yu Tsai
2016-04-04 16:22 ` [PATCH RFC 1/5] net: phy: sun8i-h3-ephy: Add bindings for Allwinner H3 Ethernet PHY Chen-Yu Tsai
2016-04-04 16:22   ` Chen-Yu Tsai
2016-04-07 17:57   ` Rob Herring
2016-04-07 17:57     ` Rob Herring
2016-04-07 17:57     ` Rob Herring
2016-04-11 19:23   ` Florian Fainelli
2016-04-11 19:23     ` Florian Fainelli
2016-04-12  1:38     ` Chen-Yu Tsai
2016-04-12  1:38       ` Chen-Yu Tsai
2016-05-07  5:30       ` Chen-Yu Tsai
2016-05-07  5:30         ` Chen-Yu Tsai
2016-05-07  5:30         ` Chen-Yu Tsai
2016-05-07  8:14         ` Hans de Goede
2016-05-07  8:14           ` Hans de Goede
2016-04-04 16:22 ` Chen-Yu Tsai [this message]
2016-04-04 16:22   ` [PATCH RFC 2/5] net: phy: sun8i-h3-ephy: Add driver " Chen-Yu Tsai
2016-04-11 19:23   ` Florian Fainelli
2016-04-11 19:23     ` Florian Fainelli
2016-04-11 19:23     ` Florian Fainelli
2016-04-14  2:04     ` Chen-Yu Tsai
2016-04-14  2:04       ` Chen-Yu Tsai
2016-04-04 16:22 ` [PATCH RFC 3/5] ARM: dts: sun8i-h3: Add H3 Ethernet PHY device node to sun8i-h3.dtsi Chen-Yu Tsai
2016-04-04 16:22   ` Chen-Yu Tsai
2016-04-04 16:22   ` Chen-Yu Tsai
2016-04-04 16:22 ` [PATCH RFC 4/5] ARM: dts: sun8i-h3: Add Ethernet controller " Chen-Yu Tsai
2016-04-04 16:22   ` Chen-Yu Tsai
2016-04-04 16:22   ` Chen-Yu Tsai
2016-04-04 16:22 ` [PATCH RFC 5/5] ARM: dts: sun8i: Enable Ethernet controller on the Orange PI PC Chen-Yu Tsai
2016-04-04 16:22   ` Chen-Yu Tsai
2016-04-11 19:25   ` Florian Fainelli
2016-04-11 19:25     ` Florian Fainelli
2016-04-11 19:25     ` Florian Fainelli

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1459786954-12649-3-git-send-email-wens@csie.org \
    --to=wens@csie.org \
    --cc=clabbe.montjoie@gmail.com \
    --cc=devicetree@vger.kernel.org \
    --cc=f.fainelli@gmail.com \
    --cc=galak@codeaurora.org \
    --cc=ijc+devicetree@hellion.org.uk \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=mark.rutland@arm.com \
    --cc=maxime.ripard@free-electrons.com \
    --cc=netdev@vger.kernel.org \
    --cc=pawel.moll@arm.com \
    --cc=robh+dt@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.