1a9770eacSAndrew Lunn // SPDX-License-Identifier: GPL-2.0+
2a9770eacSAndrew Lunn /* MDIO bus multiplexer using kernel multiplexer subsystem
3a9770eacSAndrew Lunn *
4a9770eacSAndrew Lunn * Copyright 2019 NXP
5a9770eacSAndrew Lunn */
6a9770eacSAndrew Lunn
7a9770eacSAndrew Lunn #include <linux/mdio-mux.h>
8a9770eacSAndrew Lunn #include <linux/module.h>
9a9770eacSAndrew Lunn #include <linux/mux/consumer.h>
101bf34366SCalvin Johnson #include <linux/platform_device.h>
11a9770eacSAndrew Lunn
12a9770eacSAndrew Lunn struct mdio_mux_multiplexer_state {
13a9770eacSAndrew Lunn struct mux_control *muxc;
14a9770eacSAndrew Lunn bool do_deselect;
15a9770eacSAndrew Lunn void *mux_handle;
16a9770eacSAndrew Lunn };
17a9770eacSAndrew Lunn
18a9770eacSAndrew Lunn /**
19a9770eacSAndrew Lunn * mdio_mux_multiplexer_switch_fn - This function is called by the mdio-mux
20a9770eacSAndrew Lunn * layer when it thinks the mdio bus
21a9770eacSAndrew Lunn * multiplexer needs to switch.
22a9770eacSAndrew Lunn * @current_child: current value of the mux register.
23a9770eacSAndrew Lunn * @desired_child: value of the 'reg' property of the target child MDIO node.
24a9770eacSAndrew Lunn * @data: Private data used by this switch_fn passed to mdio_mux_init function
25a9770eacSAndrew Lunn * via mdio_mux_init(.., .., .., .., data, ..).
26a9770eacSAndrew Lunn *
27a9770eacSAndrew Lunn * The first time this function is called, current_child == -1.
28a9770eacSAndrew Lunn * If current_child == desired_child, then the mux is already set to the
29a9770eacSAndrew Lunn * correct bus.
30a9770eacSAndrew Lunn */
mdio_mux_multiplexer_switch_fn(int current_child,int desired_child,void * data)31a9770eacSAndrew Lunn static int mdio_mux_multiplexer_switch_fn(int current_child, int desired_child,
32a9770eacSAndrew Lunn void *data)
33a9770eacSAndrew Lunn {
34a9770eacSAndrew Lunn struct platform_device *pdev;
35a9770eacSAndrew Lunn struct mdio_mux_multiplexer_state *s;
36a9770eacSAndrew Lunn int ret = 0;
37a9770eacSAndrew Lunn
38a9770eacSAndrew Lunn pdev = (struct platform_device *)data;
39a9770eacSAndrew Lunn s = platform_get_drvdata(pdev);
40a9770eacSAndrew Lunn
41a9770eacSAndrew Lunn if (!(current_child ^ desired_child))
42a9770eacSAndrew Lunn return 0;
43a9770eacSAndrew Lunn
44a9770eacSAndrew Lunn if (s->do_deselect)
45a9770eacSAndrew Lunn ret = mux_control_deselect(s->muxc);
46a9770eacSAndrew Lunn if (ret) {
47a9770eacSAndrew Lunn dev_err(&pdev->dev, "mux_control_deselect failed in %s: %d\n",
48a9770eacSAndrew Lunn __func__, ret);
49a9770eacSAndrew Lunn return ret;
50a9770eacSAndrew Lunn }
51a9770eacSAndrew Lunn
52a9770eacSAndrew Lunn ret = mux_control_select(s->muxc, desired_child);
53a9770eacSAndrew Lunn if (!ret) {
54a9770eacSAndrew Lunn dev_dbg(&pdev->dev, "%s %d -> %d\n", __func__, current_child,
55a9770eacSAndrew Lunn desired_child);
56a9770eacSAndrew Lunn s->do_deselect = true;
57a9770eacSAndrew Lunn } else {
58a9770eacSAndrew Lunn s->do_deselect = false;
59a9770eacSAndrew Lunn }
60a9770eacSAndrew Lunn
61a9770eacSAndrew Lunn return ret;
62a9770eacSAndrew Lunn }
63a9770eacSAndrew Lunn
mdio_mux_multiplexer_probe(struct platform_device * pdev)64a9770eacSAndrew Lunn static int mdio_mux_multiplexer_probe(struct platform_device *pdev)
65a9770eacSAndrew Lunn {
66a9770eacSAndrew Lunn struct device *dev = &pdev->dev;
67a9770eacSAndrew Lunn struct mdio_mux_multiplexer_state *s;
68a9770eacSAndrew Lunn int ret = 0;
69a9770eacSAndrew Lunn
70a9770eacSAndrew Lunn s = devm_kzalloc(&pdev->dev, sizeof(*s), GFP_KERNEL);
71a9770eacSAndrew Lunn if (!s)
72a9770eacSAndrew Lunn return -ENOMEM;
73a9770eacSAndrew Lunn
74a9770eacSAndrew Lunn s->muxc = devm_mux_control_get(dev, NULL);
75*4633b391SYang Yingliang if (IS_ERR(s->muxc))
76*4633b391SYang Yingliang return dev_err_probe(&pdev->dev, PTR_ERR(s->muxc),
77*4633b391SYang Yingliang "Failed to get mux\n");
78a9770eacSAndrew Lunn
79a9770eacSAndrew Lunn platform_set_drvdata(pdev, s);
80a9770eacSAndrew Lunn
81a9770eacSAndrew Lunn ret = mdio_mux_init(&pdev->dev, pdev->dev.of_node,
82a9770eacSAndrew Lunn mdio_mux_multiplexer_switch_fn, &s->mux_handle,
83a9770eacSAndrew Lunn pdev, NULL);
84a9770eacSAndrew Lunn
85a9770eacSAndrew Lunn return ret;
86a9770eacSAndrew Lunn }
87a9770eacSAndrew Lunn
mdio_mux_multiplexer_remove(struct platform_device * pdev)88a9770eacSAndrew Lunn static int mdio_mux_multiplexer_remove(struct platform_device *pdev)
89a9770eacSAndrew Lunn {
90a9770eacSAndrew Lunn struct mdio_mux_multiplexer_state *s = platform_get_drvdata(pdev);
91a9770eacSAndrew Lunn
92a9770eacSAndrew Lunn mdio_mux_uninit(s->mux_handle);
93a9770eacSAndrew Lunn
94a9770eacSAndrew Lunn if (s->do_deselect)
95a9770eacSAndrew Lunn mux_control_deselect(s->muxc);
96a9770eacSAndrew Lunn
97a9770eacSAndrew Lunn return 0;
98a9770eacSAndrew Lunn }
99a9770eacSAndrew Lunn
100a9770eacSAndrew Lunn static const struct of_device_id mdio_mux_multiplexer_match[] = {
101a9770eacSAndrew Lunn { .compatible = "mdio-mux-multiplexer", },
102a9770eacSAndrew Lunn {},
103a9770eacSAndrew Lunn };
104a9770eacSAndrew Lunn MODULE_DEVICE_TABLE(of, mdio_mux_multiplexer_match);
105a9770eacSAndrew Lunn
106a9770eacSAndrew Lunn static struct platform_driver mdio_mux_multiplexer_driver = {
107a9770eacSAndrew Lunn .driver = {
108a9770eacSAndrew Lunn .name = "mdio-mux-multiplexer",
109a9770eacSAndrew Lunn .of_match_table = mdio_mux_multiplexer_match,
110a9770eacSAndrew Lunn },
111a9770eacSAndrew Lunn .probe = mdio_mux_multiplexer_probe,
112a9770eacSAndrew Lunn .remove = mdio_mux_multiplexer_remove,
113a9770eacSAndrew Lunn };
114a9770eacSAndrew Lunn
115a9770eacSAndrew Lunn module_platform_driver(mdio_mux_multiplexer_driver);
116a9770eacSAndrew Lunn
117a9770eacSAndrew Lunn MODULE_DESCRIPTION("MDIO bus multiplexer using kernel multiplexer subsystem");
118a9770eacSAndrew Lunn MODULE_AUTHOR("Pankaj Bansal <pankaj.bansal@nxp.com>");
119a9770eacSAndrew Lunn MODULE_LICENSE("GPL");
120