summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/net/Kconfig8
-rw-r--r--drivers/net/Makefile1
-rw-r--r--drivers/net/mdio_mux_i2creg.c108
3 files changed, 117 insertions, 0 deletions
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 6eb22df528..084e095229 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -595,4 +595,12 @@ config FSL_ENETC
This driver supports the NXP ENETC Ethernet controller found on some
of the NXP SoCs.
+config MDIO_MUX_I2CREG
+ bool "MDIO MUX accessed as a register over I2C"
+ depends on DM_MDIO_MUX && DM_I2C
+ help
+ This driver is used for MDIO muxes driven by writing to a register of
+ an I2C chip. The board it was developed for uses a mux controlled by
+ on-board FPGA which in turn is accessed as a chip over I2C.
+
endif # NETDEVICES
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index b6e40df522..71c0889355 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -37,6 +37,7 @@ obj-$(CONFIG_LAN91C96) += lan91c96.o
obj-$(CONFIG_LPC32XX_ETH) += lpc32xx_eth.o
obj-$(CONFIG_MACB) += macb.o
obj-$(CONFIG_MCFFEC) += mcffec.o mcfmii.o
+obj-$(CONFIG_MDIO_MUX_I2CREG) += mdio_mux_i2creg.o
obj-$(CONFIG_MDIO_MUX_SANDBOX) += mdio_mux_sandbox.o
obj-$(CONFIG_MPC8XX_FEC) += mpc8xx_fec.o
obj-$(CONFIG_MT7628_ETH) += mt7628-eth.o
diff --git a/drivers/net/mdio_mux_i2creg.c b/drivers/net/mdio_mux_i2creg.c
new file mode 100644
index 0000000000..3e82898f46
--- /dev/null
+++ b/drivers/net/mdio_mux_i2creg.c
@@ -0,0 +1,108 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2019
+ * Alex Marginean, NXP
+ */
+
+#include <dm.h>
+#include <errno.h>
+#include <miiphy.h>
+#include <i2c.h>
+
+/*
+ * This driver is used for MDIO muxes driven by writing to a register of an I2C
+ * chip. The board it was developed for uses a mux controlled by on-board FPGA
+ * which in turn is accessed as a chip over I2C.
+ */
+
+struct mdio_mux_i2creg_priv {
+ struct udevice *chip;
+ int reg;
+ int mask;
+};
+
+static int mdio_mux_i2creg_select(struct udevice *mux, int cur, int sel)
+{
+ struct mdio_mux_i2creg_priv *priv = dev_get_priv(mux);
+ u8 val, val_old;
+
+ /* if last selection didn't change we're good to go */
+ if (cur == sel)
+ return 0;
+
+ val_old = dm_i2c_reg_read(priv->chip, priv->reg);
+ val = (val_old & ~priv->mask) | (sel & priv->mask);
+ debug("%s: chip %s, reg %x, val %x => %x\n", __func__, priv->chip->name,
+ priv->reg, val_old, val);
+ dm_i2c_reg_write(priv->chip, priv->reg, val);
+
+ return 0;
+}
+
+static const struct mdio_mux_ops mdio_mux_i2creg_ops = {
+ .select = mdio_mux_i2creg_select,
+};
+
+static int mdio_mux_i2creg_probe(struct udevice *dev)
+{
+ struct mdio_mux_i2creg_priv *priv = dev_get_priv(dev);
+ ofnode chip_node, bus_node;
+ struct udevice *i2c_bus;
+ u32 reg_mask[2];
+ u32 chip_addr;
+ int err;
+
+ /* read the register addr/mask pair */
+ err = dev_read_u32_array(dev, "mux-reg-masks", reg_mask, 2);
+ if (err) {
+ debug("%s: error reading mux-reg-masks property\n", __func__);
+ return err;
+ }
+
+ /* parent should be an I2C chip, grandparent should be an I2C bus */
+ chip_node = ofnode_get_parent(dev->node);
+ bus_node = ofnode_get_parent(chip_node);
+
+ err = uclass_get_device_by_ofnode(UCLASS_I2C, bus_node, &i2c_bus);
+ if (err) {
+ debug("%s: can't find I2C bus for node %s\n", __func__,
+ ofnode_get_name(bus_node));
+ return err;
+ }
+
+ err = ofnode_read_u32(chip_node, "reg", &chip_addr);
+ if (err) {
+ debug("%s: can't read chip address in %s\n", __func__,
+ ofnode_get_name(chip_node));
+ return err;
+ }
+
+ err = i2c_get_chip(i2c_bus, (uint)chip_addr, 1, &priv->chip);
+ if (err) {
+ debug("%s: can't find i2c chip device for addr %x\n", __func__,
+ chip_addr);
+ return err;
+ }
+
+ priv->reg = (int)reg_mask[0];
+ priv->mask = (int)reg_mask[1];
+
+ debug("%s: chip %s, reg %x, mask %x\n", __func__, priv->chip->name,
+ priv->reg, priv->mask);
+
+ return 0;
+}
+
+static const struct udevice_id mdio_mux_i2creg_ids[] = {
+ { .compatible = "mdio-mux-i2creg" },
+ { }
+};
+
+U_BOOT_DRIVER(mdio_mux_i2creg) = {
+ .name = "mdio_mux_i2creg",
+ .id = UCLASS_MDIO_MUX,
+ .of_match = mdio_mux_i2creg_ids,
+ .probe = mdio_mux_i2creg_probe,
+ .ops = &mdio_mux_i2creg_ops,
+ .priv_auto_alloc_size = sizeof(struct mdio_mux_i2creg_priv),
+};