xref: /openbmc/linux/drivers/net/mdio/mdio-mux-multiplexer.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
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