1 // SPDX-License-Identifier: GPL-2.0-only
3 * I2C multiplexer using pinctrl API
5 * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
9 #include <linux/i2c-mux.h>
10 #include <linux/module.h>
11 #include <linux/pinctrl/consumer.h>
12 #include <linux/platform_device.h>
13 #include <linux/slab.h>
15 #include "../../pinctrl/core.h"
17 struct i2c_mux_pinctrl
{
18 struct pinctrl
*pinctrl
;
19 struct pinctrl_state
*states
[];
22 static int i2c_mux_pinctrl_select(struct i2c_mux_core
*muxc
, u32 chan
)
24 struct i2c_mux_pinctrl
*mux
= i2c_mux_priv(muxc
);
26 return pinctrl_select_state(mux
->pinctrl
, mux
->states
[chan
]);
29 static int i2c_mux_pinctrl_deselect(struct i2c_mux_core
*muxc
, u32 chan
)
31 return i2c_mux_pinctrl_select(muxc
, muxc
->num_adapters
);
34 static struct i2c_adapter
*i2c_mux_pinctrl_root_adapter(
35 struct pinctrl_state
*state
)
37 struct i2c_adapter
*root
= NULL
;
38 struct pinctrl_setting
*setting
;
39 struct i2c_adapter
*pin_root
;
41 list_for_each_entry(setting
, &state
->settings
, node
) {
42 pin_root
= i2c_root_adapter(setting
->pctldev
->dev
);
47 else if (root
!= pin_root
)
54 static struct i2c_adapter
*i2c_mux_pinctrl_parent_adapter(struct device
*dev
)
56 struct device_node
*np
= dev
->of_node
;
57 struct device_node
*parent_np
;
58 struct i2c_adapter
*parent
;
60 parent_np
= of_parse_phandle(np
, "i2c-parent", 0);
62 dev_err(dev
, "Cannot parse i2c-parent\n");
63 return ERR_PTR(-ENODEV
);
65 parent
= of_find_i2c_adapter_by_node(parent_np
);
66 of_node_put(parent_np
);
68 return ERR_PTR(-EPROBE_DEFER
);
73 static int i2c_mux_pinctrl_probe(struct platform_device
*pdev
)
75 struct device
*dev
= &pdev
->dev
;
76 struct device_node
*np
= dev
->of_node
;
77 struct i2c_mux_core
*muxc
;
78 struct i2c_mux_pinctrl
*mux
;
79 struct i2c_adapter
*parent
;
80 struct i2c_adapter
*root
;
81 int num_names
, i
, ret
;
84 num_names
= of_property_count_strings(np
, "pinctrl-names");
86 dev_err(dev
, "Cannot parse pinctrl-names: %d\n",
91 parent
= i2c_mux_pinctrl_parent_adapter(dev
);
93 return PTR_ERR(parent
);
95 muxc
= i2c_mux_alloc(parent
, dev
, num_names
,
96 struct_size(mux
, states
, num_names
),
97 0, i2c_mux_pinctrl_select
, NULL
);
102 mux
= i2c_mux_priv(muxc
);
104 platform_set_drvdata(pdev
, muxc
);
106 mux
->pinctrl
= devm_pinctrl_get(dev
);
107 if (IS_ERR(mux
->pinctrl
)) {
108 ret
= PTR_ERR(mux
->pinctrl
);
109 dev_err(dev
, "Cannot get pinctrl: %d\n", ret
);
113 for (i
= 0; i
< num_names
; i
++) {
114 ret
= of_property_read_string_index(np
, "pinctrl-names", i
,
117 dev_err(dev
, "Cannot parse pinctrl-names: %d\n", ret
);
121 mux
->states
[i
] = pinctrl_lookup_state(mux
->pinctrl
, name
);
122 if (IS_ERR(mux
->states
[i
])) {
123 ret
= PTR_ERR(mux
->states
[i
]);
124 dev_err(dev
, "Cannot look up pinctrl state %s: %d\n",
129 if (strcmp(name
, "idle"))
132 if (i
!= num_names
- 1) {
133 dev_err(dev
, "idle state must be last\n");
137 muxc
->deselect
= i2c_mux_pinctrl_deselect
;
140 root
= i2c_root_adapter(&muxc
->parent
->dev
);
142 muxc
->mux_locked
= true;
143 for (i
= 0; i
< num_names
; i
++) {
144 if (root
!= i2c_mux_pinctrl_root_adapter(mux
->states
[i
])) {
145 muxc
->mux_locked
= false;
149 if (muxc
->mux_locked
)
150 dev_info(dev
, "mux-locked i2c mux\n");
152 /* Do not add any adapter for the idle state (if it's there at all). */
153 for (i
= 0; i
< num_names
- !!muxc
->deselect
; i
++) {
154 ret
= i2c_mux_add_adapter(muxc
, 0, i
, 0);
156 goto err_del_adapter
;
162 i2c_mux_del_adapters(muxc
);
164 i2c_put_adapter(parent
);
169 static int i2c_mux_pinctrl_remove(struct platform_device
*pdev
)
171 struct i2c_mux_core
*muxc
= platform_get_drvdata(pdev
);
173 i2c_mux_del_adapters(muxc
);
174 i2c_put_adapter(muxc
->parent
);
179 static const struct of_device_id i2c_mux_pinctrl_of_match
[] = {
180 { .compatible
= "i2c-mux-pinctrl", },
183 MODULE_DEVICE_TABLE(of
, i2c_mux_pinctrl_of_match
);
185 static struct platform_driver i2c_mux_pinctrl_driver
= {
187 .name
= "i2c-mux-pinctrl",
188 .of_match_table
= of_match_ptr(i2c_mux_pinctrl_of_match
),
190 .probe
= i2c_mux_pinctrl_probe
,
191 .remove
= i2c_mux_pinctrl_remove
,
193 module_platform_driver(i2c_mux_pinctrl_driver
);
195 MODULE_DESCRIPTION("pinctrl-based I2C multiplexer driver");
196 MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
197 MODULE_LICENSE("GPL v2");
198 MODULE_ALIAS("platform:i2c-mux-pinctrl");