Add support for Generic Mux controls, when Mdio mux node is a consumer
of mux produced by some other device.

Signed-off-by: Pankaj Bansal <pankaj.ban...@nxp.com>
---
 drivers/net/phy/Kconfig    |  1 +
 drivers/net/phy/mdio-mux.c | 81 ++++++++++++++++++++++++++++++++++++
 2 files changed, 82 insertions(+)

diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
index 3d187cd50eb0..e0e6b2b33d6d 100644
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -47,6 +47,7 @@ config MDIO_BITBANG
 config MDIO_BUS_MUX
        tristate
        depends on OF_MDIO
+       select MULTIPLEXER
        help
          This module provides a driver framework for MDIO bus
          multiplexers which connect one of several child MDIO busses
diff --git a/drivers/net/phy/mdio-mux.c b/drivers/net/phy/mdio-mux.c
index 0a86f1e4c02f..c49e4ae9c174 100644
--- a/drivers/net/phy/mdio-mux.c
+++ b/drivers/net/phy/mdio-mux.c
@@ -11,6 +11,7 @@
 #include <linux/of_mdio.h>
 #include <linux/device.h>
 #include <linux/module.h>
+#include <linux/mux/consumer.h>
 #include <linux/phy.h>
 
 #define DRV_DESCRIPTION "MDIO bus multiplexer driver"
@@ -35,6 +36,11 @@ struct mdio_mux_child_bus {
        int bus_number;
 };
 
+struct mdio_mux_state {
+       struct mux_control *muxc;
+       void *mux_handle;
+};
+
 /*
  * The parent bus' lock is used to order access to the switch_fn.
  */
@@ -208,6 +214,81 @@ void mdio_mux_uninit(void *mux_handle)
 }
 EXPORT_SYMBOL_GPL(mdio_mux_uninit);
 
+static int mdio_mux_switch_fn(int current_child, int desired_child, void *data)
+{
+       struct platform_device *pdev;
+       struct mdio_mux_state *s;
+       int ret = 0;
+
+       pdev = (struct platform_device *)data;
+       s = (struct mdio_mux_state *)platform_get_drvdata(pdev);
+       if (current_child ^ desired_child) {
+               if (current_child != -1)
+                       ret = mux_control_deselect(s->muxc);
+               if (ret)
+                       return ret;
+
+               ret =  mux_control_select(s->muxc, desired_child);
+               if (!ret)
+                       dev_dbg(&pdev->dev, "%s %d -> %d\n", __func__,
+                               current_child, desired_child);
+       }
+
+       return ret;
+}
+
+static int mdio_mux_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct mdio_mux_state *s;
+       int ret = 0;
+
+       s = devm_kzalloc(&pdev->dev, sizeof(*s), GFP_KERNEL);
+       if (!s)
+               return -ENOMEM;
+
+       s->muxc = devm_mux_control_get(dev, NULL);
+       if (IS_ERR(s->muxc)) {
+               ret = PTR_ERR(s->muxc);
+               if (ret != -EPROBE_DEFER)
+                       dev_err(&pdev->dev, "Failed to get mux: %d\n", ret);
+               return ret;
+       }
+
+       platform_set_drvdata(pdev, s);
+
+       ret = mdio_mux_init(&pdev->dev, pdev->dev.of_node, mdio_mux_switch_fn,
+                           &s->mux_handle, pdev, NULL);
+
+       return ret;
+}
+
+static int mdio_mux_remove(struct platform_device *pdev)
+{
+       struct mdio_mux_state *s = platform_get_drvdata(pdev);
+
+       mdio_mux_uninit(s->mux_handle);
+
+       return 0;
+}
+
+static const struct of_device_id mdio_mux_match[] = {
+       { .compatible = "mdio-mux", },
+       {},
+};
+MODULE_DEVICE_TABLE(of, mdio_mux_match);
+
+static struct platform_driver mdio_mux_driver = {
+       .driver = {
+               .name           = "mdio-mux",
+               .of_match_table = mdio_mux_match,
+       },
+       .probe          = mdio_mux_probe,
+       .remove         = mdio_mux_remove,
+};
+
+module_platform_driver(mdio_mux_driver);
+
 MODULE_DESCRIPTION(DRV_DESCRIPTION);
 MODULE_AUTHOR("David Daney");
 MODULE_LICENSE("GPL");
-- 
2.17.1

Reply via email to