diff mbox

[v2] phy: Add MOXA MDIO driver

Message ID 1383666901-22354-1-git-send-email-jonas.jensen@gmail.com (mailing list archive)
State New, archived
Headers show

Commit Message

Jonas Jensen Nov. 5, 2013, 3:55 p.m. UTC
The MOXA UC-711X hardware(s) has an ethernet controller that seem
to be developed internally. The IC used is "RTL8201CP".

This patch adds an MDIO driver which handles the MII bus.

Signed-off-by: Jonas Jensen <jonas.jensen@gmail.com>
---

Notes:
    Thanks for the feedback.
    
    Besides what is below I plan to submit changes suggested by
    Grant as a separate patch (to Documentation/devicetree/bindings/net/phy.txt)
    
    Changes since v1:
    
    1. split changes to drivers/net/phy/realtek.c into separate patch
    
    driver code:
    2. use dev_dbg() instead of dev_err()
    3. use id "%s-%d-mii" instead of "%s-mii"
    4. call mdiobus_free() on of_mdiobus_register() failure
    
    DT binding document:
    5. remove it, submitting changes (to Documentation/devicetree/bindings/net/moxa,moxart-mac.txt) along with changes to the driver
    
    Applies to next-20131105

 drivers/net/phy/Kconfig       |   7 ++
 drivers/net/phy/Makefile      |   1 +
 drivers/net/phy/mdio-moxart.c | 201 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 209 insertions(+)
 create mode 100644 drivers/net/phy/mdio-moxart.c

Comments

David Miller Nov. 7, 2013, 8:37 p.m. UTC | #1
From: Jonas Jensen <jonas.jensen@gmail.com>
Date: Tue,  5 Nov 2013 16:55:01 +0100

> The MOXA UC-711X hardware(s) has an ethernet controller that seem
> to be developed internally. The IC used is "RTL8201CP".
> 
> This patch adds an MDIO driver which handles the MII bus.
> 
> Signed-off-by: Jonas Jensen <jonas.jensen@gmail.com>

Applied, thanks Jonas.
diff mbox

Patch

diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
index 342561a..9b5d46c 100644
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -154,6 +154,13 @@  config MDIO_SUN4I
 	  interface units of the Allwinner SoC that have an EMAC (A10,
 	  A12, A10s, etc.)
 
+config MDIO_MOXART
+        tristate "MOXA ART MDIO interface support"
+        depends on ARCH_MOXART
+        help
+          This driver supports the MDIO interface found in the network
+          interface units of the MOXA ART SoC
+
 config MDIO_BUS_MUX
 	tristate
 	depends on OF_MDIO
diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
index 23a2ab2..9013dfa 100644
--- a/drivers/net/phy/Makefile
+++ b/drivers/net/phy/Makefile
@@ -31,3 +31,4 @@  obj-$(CONFIG_MDIO_BUS_MUX)	+= mdio-mux.o
 obj-$(CONFIG_MDIO_BUS_MUX_GPIO)	+= mdio-mux-gpio.o
 obj-$(CONFIG_MDIO_BUS_MUX_MMIOREG) += mdio-mux-mmioreg.o
 obj-$(CONFIG_MDIO_SUN4I)	+= mdio-sun4i.o
+obj-$(CONFIG_MDIO_MOXART)	+= mdio-moxart.o
diff --git a/drivers/net/phy/mdio-moxart.c b/drivers/net/phy/mdio-moxart.c
new file mode 100644
index 0000000..a5741cb
--- /dev/null
+++ b/drivers/net/phy/mdio-moxart.c
@@ -0,0 +1,201 @@ 
+/* MOXA ART Ethernet (RTL8201CP) MDIO interface driver
+ *
+ * Copyright (C) 2013 Jonas Jensen <jonas.jensen@gmail.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2.  This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of_address.h>
+#include <linux/of_mdio.h>
+#include <linux/phy.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+
+#define REG_PHY_CTRL            0
+#define REG_PHY_WRITE_DATA      4
+
+/* REG_PHY_CTRL */
+#define MIIWR                   BIT(27) /* init write sequence (auto cleared)*/
+#define MIIRD                   BIT(26)
+#define REGAD_MASK              0x3e00000
+#define PHYAD_MASK              0x1f0000
+#define MIIRDATA_MASK           0xffff
+
+/* REG_PHY_WRITE_DATA */
+#define MIIWDATA_MASK           0xffff
+
+struct moxart_mdio_data {
+	void __iomem		*base;
+};
+
+static int moxart_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
+{
+	struct moxart_mdio_data *data = bus->priv;
+	u32 ctrl = 0;
+	unsigned int count = 5;
+
+	dev_dbg(&bus->dev, "%s\n", __func__);
+
+	ctrl |= MIIRD | ((mii_id << 16) & PHYAD_MASK) |
+		((regnum << 21) & REGAD_MASK);
+
+	writel(ctrl, data->base + REG_PHY_CTRL);
+
+	do {
+		ctrl = readl(data->base + REG_PHY_CTRL);
+
+		if (!(ctrl & MIIRD))
+			return ctrl & MIIRDATA_MASK;
+
+		mdelay(10);
+		count--;
+	} while (count > 0);
+
+	dev_dbg(&bus->dev, "%s timed out\n", __func__);
+
+	return -ETIMEDOUT;
+}
+
+static int moxart_mdio_write(struct mii_bus *bus, int mii_id,
+			     int regnum, u16 value)
+{
+	struct moxart_mdio_data *data = bus->priv;
+	u32 ctrl = 0;
+	unsigned int count = 5;
+
+	dev_dbg(&bus->dev, "%s\n", __func__);
+
+	ctrl |= MIIWR | ((mii_id << 16) & PHYAD_MASK) |
+		((regnum << 21) & REGAD_MASK);
+
+	value &= MIIWDATA_MASK;
+
+	writel(value, data->base + REG_PHY_WRITE_DATA);
+	writel(ctrl, data->base + REG_PHY_CTRL);
+
+	do {
+		ctrl = readl(data->base + REG_PHY_CTRL);
+
+		if (!(ctrl & MIIWR))
+			return 0;
+
+		mdelay(10);
+		count--;
+	} while (count > 0);
+
+	dev_dbg(&bus->dev, "%s timed out\n", __func__);
+
+	return -ETIMEDOUT;
+}
+
+static int moxart_mdio_reset(struct mii_bus *bus)
+{
+	int data, i;
+
+	for (i = 0; i < PHY_MAX_ADDR; i++) {
+		data = moxart_mdio_read(bus, i, MII_BMCR);
+		if (data < 0)
+			continue;
+
+		data |= BMCR_RESET;
+		if (moxart_mdio_write(bus, i, MII_BMCR, data) < 0)
+			continue;
+	}
+
+	return 0;
+}
+
+static int moxart_mdio_probe(struct platform_device *pdev)
+{
+	struct device_node *np = pdev->dev.of_node;
+	struct mii_bus *bus;
+	struct moxart_mdio_data *data;
+	struct resource *res;
+	int ret, i;
+
+	bus = mdiobus_alloc_size(sizeof(*data));
+	if (!bus)
+		return -ENOMEM;
+
+	bus->name = "MOXA ART Ethernet MII";
+	bus->read = &moxart_mdio_read;
+	bus->write = &moxart_mdio_write;
+	bus->reset = &moxart_mdio_reset;
+	snprintf(bus->id, MII_BUS_ID_SIZE, "%s-%d-mii", pdev->name, pdev->id);
+	bus->parent = &pdev->dev;
+
+	bus->irq = devm_kzalloc(&pdev->dev, sizeof(int) * PHY_MAX_ADDR,
+			GFP_KERNEL);
+	if (!bus->irq) {
+		ret = -ENOMEM;
+		goto err_out_free_mdiobus;
+	}
+
+	/* Setting PHY_IGNORE_INTERRUPT here even if it has no effect,
+	 * of_mdiobus_register() sets these PHY_POLL.
+	 * Ideally, the interrupt from MAC controller could be used to
+	 * detect link state changes, not polling, i.e. if there was
+	 * a way phy_driver could set PHY_HAS_INTERRUPT but have that
+	 * interrupt handled in ethernet drivercode.
+	 */
+	for (i = 0; i < PHY_MAX_ADDR; i++)
+		bus->irq[i] = PHY_IGNORE_INTERRUPT;
+
+	data = bus->priv;
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	data->base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(data->base)) {
+		ret = PTR_ERR(data->base);
+		goto err_out_free_mdiobus;
+	}
+
+	ret = of_mdiobus_register(bus, np);
+	if (ret < 0)
+		goto err_out_free_mdiobus;
+
+	platform_set_drvdata(pdev, bus);
+
+	return 0;
+
+err_out_free_mdiobus:
+	mdiobus_free(bus);
+	return ret;
+}
+
+static int moxart_mdio_remove(struct platform_device *pdev)
+{
+	struct mii_bus *bus = platform_get_drvdata(pdev);
+
+	mdiobus_unregister(bus);
+	mdiobus_free(bus);
+
+	return 0;
+}
+
+static const struct of_device_id moxart_mdio_dt_ids[] = {
+	{ .compatible = "moxa,moxart-mdio" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, moxart_mdio_dt_ids);
+
+static struct platform_driver moxart_mdio_driver = {
+	.probe = moxart_mdio_probe,
+	.remove = moxart_mdio_remove,
+	.driver = {
+		.name = "moxart-mdio",
+		.of_match_table = moxart_mdio_dt_ids,
+	},
+};
+
+module_platform_driver(moxart_mdio_driver);
+
+MODULE_DESCRIPTION("MOXA ART MDIO interface driver");
+MODULE_AUTHOR("Jonas Jensen <jonas.jensen@gmail.com>");
+MODULE_LICENSE("GPL");