[PATCH] Net: add mdio_i2c driver.

Clément Leger cleger at kalray.eu
Tue Nov 14 05:34:51 PST 2017


>From 1238b5c70d29762074bc71ac25623b958af9010a Mon Sep 17 00:00:00 2001
From: Clement Leger <clement.leger at kalray.eu>
Date: Wed, 8 Nov 2017 09:13:10 +0100
Subject: [PATCH] Net: add mdio_i2c driver (i2c to mdio bridge).

Some phys (Marvells) can be connected using and i2c bus instead of a mdio bus.
The communication format is then slightly different than standard mdio.
This patch add a i2c to mdio bridge which allows to communicate with
these phys.

---
 drivers/net/phy/Kconfig       |   8 +++
 drivers/net/phy/Makefile      |   1 +
 drivers/net/phy/mdio-i2c.c    | 148 ++++++++++++++++++++++++++++++++++++++++++
 dts/Bindings/net/mdio-i2c.txt |  19 ++++++
 4 files changed, 176 insertions(+)
 create mode 100644 drivers/net/phy/mdio-i2c.c
 create mode 100644 dts/Bindings/net/mdio-i2c.txt

diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
index ea2e062..f058f38 100644
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -60,6 +60,14 @@ config MDIO_BITBANG
 
           If in doubt, say N.
 
+config MDIO_I2C
+	bool "Support for I2C based MDIO buses"
+	depends on I2C
+	---help---
+	  Support I2C based PHYs. This provides a MDIO bus bridged
+	  to I2C to allow PHYs connected in I2C mode to be accessed
+	  using the existing infrastructure.
+
 config MDIO_GPIO
 	bool "Support for GPIO lib-based bitbanged MDIO buses"
 	depends on MDIO_BITBANG && GPIOLIB
diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
index 13b8f65..9f61ae6 100644
--- a/drivers/net/phy/Makefile
+++ b/drivers/net/phy/Makefile
@@ -10,3 +10,4 @@ obj-$(CONFIG_SMSC_PHY)		+= smsc.o
 obj-$(CONFIG_MDIO_MVEBU)	+= mdio-mvebu.o
 obj-$(CONFIG_MDIO_BITBANG)	+= mdio-bitbang.o
 obj-$(CONFIG_MDIO_GPIO)		+= mdio-gpio.o
+obj-$(CONFIG_MDIO_I2C)		+= mdio-i2c.o
diff --git a/drivers/net/phy/mdio-i2c.c b/drivers/net/phy/mdio-i2c.c
new file mode 100644
index 0000000..fcc5562
--- /dev/null
+++ b/drivers/net/phy/mdio-i2c.c
@@ -0,0 +1,148 @@
+/*
+ * MDIO I2C bridge
+ *
+ * Copyright (C) 2017 Clément Léger
+ *
+ * Based on code by Russell King from Linux
+ * Copyright (C) 2015 Russell King
+ *
+ * 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.
+ */
+
+#include <common.h>
+#include <driver.h>
+#include <init.h>
+#include <io.h>
+#include <of.h>
+#include <of_gpio.h>
+#include <linux/phy.h>
+#include <gpio.h>
+#include <malloc.h>
+#include <xfuncs.h>
+
+#include <i2c/i2c.h>
+
+/*
+ * I2C bus addresses 0x50 and 0x51 are normally an EEPROM, which is
+ * specified to be present in SFP modules.  These correspond with PHY
+ * addresses 16 and 17.  Disallow access to these "phy" addresses.
+ */
+static bool i2c_mii_valid_phy_id(int phy_id)
+{
+	return phy_id != 0x10 && phy_id != 0x11;
+}
+
+static unsigned int i2c_mii_phy_addr(int phy_id)
+{
+	return phy_id + 0x40;
+}
+
+static int i2c_mii_read(struct mii_bus *bus, int phy_id, int reg)
+{
+	struct i2c_adapter *i2c = bus->priv;
+	struct i2c_msg msgs[2];
+	u8 data[2], dev_addr = reg;
+	int bus_addr, ret;
+
+	if (!i2c_mii_valid_phy_id(phy_id))
+		return 0xffff;
+
+	bus_addr = i2c_mii_phy_addr(phy_id);
+	msgs[0].addr = bus_addr;
+	msgs[0].flags = 0;
+	msgs[0].len = 1;
+	msgs[0].buf = &dev_addr;
+	msgs[1].addr = bus_addr;
+	msgs[1].flags = I2C_M_RD;
+	msgs[1].len = sizeof(data);
+	msgs[1].buf = data;
+
+	ret = i2c_transfer(i2c, msgs, ARRAY_SIZE(msgs));
+	if (ret != ARRAY_SIZE(msgs))
+		return 0xffff;
+
+	return data[0] << 8 | data[1];
+}
+
+static int i2c_mii_write(struct mii_bus *bus, int phy_id, int reg, u16 val)
+{
+	struct i2c_adapter *i2c = bus->priv;
+	struct i2c_msg msg;
+	int ret;
+	u8 data[3];
+
+	if (!i2c_mii_valid_phy_id(phy_id))
+		return 0;
+
+	data[0] = reg;
+	data[1] = val >> 8;
+	data[2] = val;
+
+	msg.addr = i2c_mii_phy_addr(phy_id);
+	msg.flags = 0;
+	msg.len = 3;
+	msg.buf = data;
+
+	ret = i2c_transfer(i2c, &msg, 1);
+
+	return ret < 0 ? ret : 0;
+}
+
+static int mdio_i2c_probe(struct device_d *dev)
+{
+	struct mii_bus *mii;
+	struct i2c_adapter *i2c_adap;
+	struct device_node *i2c_node;
+
+	i2c_node = of_parse_phandle(dev->device_node, "i2c-bus", 0);
+	if (!i2c_node) {
+		dev_err(dev, "Missing i2c-bus property for mdio-i2c\n");
+		return -EINVAL;
+	}
+
+	i2c_adap = of_find_i2c_adapter_by_node(i2c_node);
+	if (!i2c_adap) {
+		dev_dbg(dev, "i2c bus not yet probed\n");
+		return -EPROBE_DEFER;
+	}
+
+	mii = xzalloc(sizeof(*mii));
+
+	mii->parent = dev;
+	mii->dev.device_node = dev->device_node;
+
+	mii->parent = dev;
+	mii->read = i2c_mii_read;
+	mii->write = i2c_mii_write;
+	mii->priv = i2c_adap;
+
+	dev->priv = mii;
+
+	dev_info(dev, "I2C mdio register\n");
+
+	return mdiobus_register(mii);
+}
+
+static void mdio_i2c_remove(struct device_d *dev)
+{
+	struct mii_bus *mii = dev->priv;
+
+	mdiobus_unregister(mii);
+	kfree(mii);
+}
+
+static const struct of_device_id i2c_mdio_dt_ids[] = {
+	{ .compatible = "virtual,mdio-i2c", },
+	{ /* sentinel */ }
+};
+
+static struct driver_d mdio_i2c_driver = {
+	.name = "mdio-i2c",
+	.probe = mdio_i2c_probe,
+	.remove = mdio_i2c_remove,
+	.of_compatible = DRV_OF_COMPAT(i2c_mdio_dt_ids),
+};
+
+device_platform_driver(mdio_i2c_driver);
diff --git a/dts/Bindings/net/mdio-i2c.txt b/dts/Bindings/net/mdio-i2c.txt
new file mode 100644
index 0000000..64e20e1
--- /dev/null
+++ b/dts/Bindings/net/mdio-i2c.txt
@@ -0,0 +1,19 @@
+MDIO on I2C Bus
+This compatible allows to use phy devices connected to an i2c bus
+and expecting to be use with mdio protocol.
+
+Currently defined compatibles:
+- virtual,mdio-i2c
+
+Required properties:
+- i2c-bus: A phandle on the I2C bus acting as a MDIO bridge where phys
+           are connected to.
+
+Example:
+
+mdio0 at i2c {
+	compatible = "virtual,mdio-i2c";
+	#address-cells = <1>;
+	#size-cells = <0>;
+	i2c-bus = <&i2c_bus>;
+};
-- 
1.8.3.1




More information about the barebox mailing list