1ae58d1e4SStephen Warren /* 2ae58d1e4SStephen Warren * I2C multiplexer using pinctrl API 3ae58d1e4SStephen Warren * 4ae58d1e4SStephen Warren * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. 5ae58d1e4SStephen Warren * 6ae58d1e4SStephen Warren * This program is free software; you can redistribute it and/or modify it 7ae58d1e4SStephen Warren * under the terms and conditions of the GNU General Public License, 8ae58d1e4SStephen Warren * version 2, as published by the Free Software Foundation. 9ae58d1e4SStephen Warren * 10ae58d1e4SStephen Warren * This program is distributed in the hope it will be useful, but WITHOUT 11ae58d1e4SStephen Warren * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12ae58d1e4SStephen Warren * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 13ae58d1e4SStephen Warren * more details. 14ae58d1e4SStephen Warren * 15ae58d1e4SStephen Warren * You should have received a copy of the GNU General Public License 16ae58d1e4SStephen Warren * along with this program. If not, see <http://www.gnu.org/licenses/>. 17ae58d1e4SStephen Warren */ 18ae58d1e4SStephen Warren 19ae58d1e4SStephen Warren #include <linux/i2c.h> 20ae58d1e4SStephen Warren #include <linux/i2c-mux.h> 21ae58d1e4SStephen Warren #include <linux/module.h> 22ae58d1e4SStephen Warren #include <linux/pinctrl/consumer.h> 23ae58d1e4SStephen Warren #include <linux/platform_device.h> 24ae58d1e4SStephen Warren #include <linux/slab.h> 254edd65e6SSachin Kamat #include <linux/of.h> 266ef91fccSPeter Rosin #include "../../pinctrl/core.h" 27ae58d1e4SStephen Warren 28ae58d1e4SStephen Warren struct i2c_mux_pinctrl { 29ae58d1e4SStephen Warren struct pinctrl *pinctrl; 30ae58d1e4SStephen Warren struct pinctrl_state **states; 31ae58d1e4SStephen Warren }; 32ae58d1e4SStephen Warren 334bbe7fb0SPeter Rosin static int i2c_mux_pinctrl_select(struct i2c_mux_core *muxc, u32 chan) 34ae58d1e4SStephen Warren { 354bbe7fb0SPeter Rosin struct i2c_mux_pinctrl *mux = i2c_mux_priv(muxc); 36ae58d1e4SStephen Warren 37ae58d1e4SStephen Warren return pinctrl_select_state(mux->pinctrl, mux->states[chan]); 38ae58d1e4SStephen Warren } 39ae58d1e4SStephen Warren 404bbe7fb0SPeter Rosin static int i2c_mux_pinctrl_deselect(struct i2c_mux_core *muxc, u32 chan) 41ae58d1e4SStephen Warren { 42*fc204671SPeter Rosin return i2c_mux_pinctrl_select(muxc, muxc->num_adapters); 43ae58d1e4SStephen Warren } 44ae58d1e4SStephen Warren 456ef91fccSPeter Rosin static struct i2c_adapter *i2c_mux_pinctrl_root_adapter( 466ef91fccSPeter Rosin struct pinctrl_state *state) 476ef91fccSPeter Rosin { 486ef91fccSPeter Rosin struct i2c_adapter *root = NULL; 496ef91fccSPeter Rosin struct pinctrl_setting *setting; 506ef91fccSPeter Rosin struct i2c_adapter *pin_root; 516ef91fccSPeter Rosin 526ef91fccSPeter Rosin list_for_each_entry(setting, &state->settings, node) { 536ef91fccSPeter Rosin pin_root = i2c_root_adapter(setting->pctldev->dev); 546ef91fccSPeter Rosin if (!pin_root) 556ef91fccSPeter Rosin return NULL; 566ef91fccSPeter Rosin if (!root) 576ef91fccSPeter Rosin root = pin_root; 586ef91fccSPeter Rosin else if (root != pin_root) 596ef91fccSPeter Rosin return NULL; 606ef91fccSPeter Rosin } 616ef91fccSPeter Rosin 626ef91fccSPeter Rosin return root; 636ef91fccSPeter Rosin } 646ef91fccSPeter Rosin 65c4aee3e1SPeter Rosin static struct i2c_adapter *i2c_mux_pinctrl_parent_adapter(struct device *dev) 66c4aee3e1SPeter Rosin { 67c4aee3e1SPeter Rosin struct device_node *np = dev->of_node; 68c4aee3e1SPeter Rosin struct device_node *parent_np; 69c4aee3e1SPeter Rosin struct i2c_adapter *parent; 70c4aee3e1SPeter Rosin 71c4aee3e1SPeter Rosin parent_np = of_parse_phandle(np, "i2c-parent", 0); 72c4aee3e1SPeter Rosin if (!parent_np) { 73c4aee3e1SPeter Rosin dev_err(dev, "Cannot parse i2c-parent\n"); 74c4aee3e1SPeter Rosin return ERR_PTR(-ENODEV); 75c4aee3e1SPeter Rosin } 76c4aee3e1SPeter Rosin parent = of_find_i2c_adapter_by_node(parent_np); 77c4aee3e1SPeter Rosin of_node_put(parent_np); 78c4aee3e1SPeter Rosin if (!parent) 79c4aee3e1SPeter Rosin return ERR_PTR(-EPROBE_DEFER); 80c4aee3e1SPeter Rosin 81c4aee3e1SPeter Rosin return parent; 82c4aee3e1SPeter Rosin } 83c4aee3e1SPeter Rosin 840b255e92SBill Pemberton static int i2c_mux_pinctrl_probe(struct platform_device *pdev) 85ae58d1e4SStephen Warren { 86c4aee3e1SPeter Rosin struct device *dev = &pdev->dev; 87c4aee3e1SPeter Rosin struct device_node *np = dev->of_node; 884bbe7fb0SPeter Rosin struct i2c_mux_core *muxc; 89ae58d1e4SStephen Warren struct i2c_mux_pinctrl *mux; 90c4aee3e1SPeter Rosin struct i2c_adapter *parent; 916ef91fccSPeter Rosin struct i2c_adapter *root; 92c4aee3e1SPeter Rosin int num_names, i, ret; 93c4aee3e1SPeter Rosin const char *name; 94ae58d1e4SStephen Warren 95c4aee3e1SPeter Rosin num_names = of_property_count_strings(np, "pinctrl-names"); 96c4aee3e1SPeter Rosin if (num_names < 0) { 97c4aee3e1SPeter Rosin dev_err(dev, "Cannot parse pinctrl-names: %d\n", 98c4aee3e1SPeter Rosin num_names); 99c4aee3e1SPeter Rosin return num_names; 100ae58d1e4SStephen Warren } 101ae58d1e4SStephen Warren 102c4aee3e1SPeter Rosin parent = i2c_mux_pinctrl_parent_adapter(dev); 103c4aee3e1SPeter Rosin if (IS_ERR(parent)) 104c4aee3e1SPeter Rosin return PTR_ERR(parent); 105ae58d1e4SStephen Warren 106c4aee3e1SPeter Rosin muxc = i2c_mux_alloc(parent, dev, num_names, 107c4aee3e1SPeter Rosin sizeof(*mux) + num_names * sizeof(*mux->states), 108c4aee3e1SPeter Rosin 0, i2c_mux_pinctrl_select, NULL); 1094bbe7fb0SPeter Rosin if (!muxc) { 110ae58d1e4SStephen Warren ret = -ENOMEM; 111c4aee3e1SPeter Rosin goto err_put_parent; 112ae58d1e4SStephen Warren } 113c4aee3e1SPeter Rosin mux = i2c_mux_priv(muxc); 114c4aee3e1SPeter Rosin mux->states = (struct pinctrl_state **)(mux + 1); 1154bbe7fb0SPeter Rosin 1164bbe7fb0SPeter Rosin platform_set_drvdata(pdev, muxc); 117ae58d1e4SStephen Warren 118c4aee3e1SPeter Rosin mux->pinctrl = devm_pinctrl_get(dev); 119ae58d1e4SStephen Warren if (IS_ERR(mux->pinctrl)) { 120ae58d1e4SStephen Warren ret = PTR_ERR(mux->pinctrl); 121c4aee3e1SPeter Rosin dev_err(dev, "Cannot get pinctrl: %d\n", ret); 122c4aee3e1SPeter Rosin goto err_put_parent; 123ae58d1e4SStephen Warren } 124c4aee3e1SPeter Rosin 125c4aee3e1SPeter Rosin for (i = 0; i < num_names; i++) { 126c4aee3e1SPeter Rosin ret = of_property_read_string_index(np, "pinctrl-names", i, 127c4aee3e1SPeter Rosin &name); 128c4aee3e1SPeter Rosin if (ret < 0) { 129c4aee3e1SPeter Rosin dev_err(dev, "Cannot parse pinctrl-names: %d\n", ret); 130c4aee3e1SPeter Rosin goto err_put_parent; 131c4aee3e1SPeter Rosin } 132c4aee3e1SPeter Rosin 133c4aee3e1SPeter Rosin mux->states[i] = pinctrl_lookup_state(mux->pinctrl, name); 134ae58d1e4SStephen Warren if (IS_ERR(mux->states[i])) { 135ae58d1e4SStephen Warren ret = PTR_ERR(mux->states[i]); 136c4aee3e1SPeter Rosin dev_err(dev, "Cannot look up pinctrl state %s: %d\n", 137c4aee3e1SPeter Rosin name, ret); 138c4aee3e1SPeter Rosin goto err_put_parent; 139ae58d1e4SStephen Warren } 140ae58d1e4SStephen Warren 141c4aee3e1SPeter Rosin if (strcmp(name, "idle")) 142c4aee3e1SPeter Rosin continue; 143c4aee3e1SPeter Rosin 144c4aee3e1SPeter Rosin if (i != num_names - 1) { 145c4aee3e1SPeter Rosin dev_err(dev, "idle state must be last\n"); 146c4aee3e1SPeter Rosin ret = -EINVAL; 147c4aee3e1SPeter Rosin goto err_put_parent; 148c4aee3e1SPeter Rosin } 1494bbe7fb0SPeter Rosin muxc->deselect = i2c_mux_pinctrl_deselect; 150ae58d1e4SStephen Warren } 151ae58d1e4SStephen Warren 1526ef91fccSPeter Rosin root = i2c_root_adapter(&muxc->parent->dev); 1536ef91fccSPeter Rosin 1546ef91fccSPeter Rosin muxc->mux_locked = true; 155c4aee3e1SPeter Rosin for (i = 0; i < num_names; i++) { 1566ef91fccSPeter Rosin if (root != i2c_mux_pinctrl_root_adapter(mux->states[i])) { 1576ef91fccSPeter Rosin muxc->mux_locked = false; 1586ef91fccSPeter Rosin break; 1596ef91fccSPeter Rosin } 1606ef91fccSPeter Rosin } 1616ef91fccSPeter Rosin if (muxc->mux_locked) 162c4aee3e1SPeter Rosin dev_info(dev, "mux-locked i2c mux\n"); 1636ef91fccSPeter Rosin 164c4aee3e1SPeter Rosin /* Do not add any adapter for the idle state (if it's there at all). */ 165*fc204671SPeter Rosin for (i = 0; i < num_names - !!muxc->deselect; i++) { 166c4aee3e1SPeter Rosin ret = i2c_mux_add_adapter(muxc, 0, i, 0); 167c99a23e5SPeter Rosin if (ret) 168ae58d1e4SStephen Warren goto err_del_adapter; 169ae58d1e4SStephen Warren } 170ae58d1e4SStephen Warren 171ae58d1e4SStephen Warren return 0; 172ae58d1e4SStephen Warren 173ae58d1e4SStephen Warren err_del_adapter: 1744bbe7fb0SPeter Rosin i2c_mux_del_adapters(muxc); 175c4aee3e1SPeter Rosin err_put_parent: 1764bbe7fb0SPeter Rosin i2c_put_adapter(muxc->parent); 177c4aee3e1SPeter Rosin 178ae58d1e4SStephen Warren return ret; 179ae58d1e4SStephen Warren } 180ae58d1e4SStephen Warren 1810b255e92SBill Pemberton static int i2c_mux_pinctrl_remove(struct platform_device *pdev) 182ae58d1e4SStephen Warren { 1834bbe7fb0SPeter Rosin struct i2c_mux_core *muxc = platform_get_drvdata(pdev); 184ae58d1e4SStephen Warren 1854bbe7fb0SPeter Rosin i2c_mux_del_adapters(muxc); 1864bbe7fb0SPeter Rosin i2c_put_adapter(muxc->parent); 187c4aee3e1SPeter Rosin 188ae58d1e4SStephen Warren return 0; 189ae58d1e4SStephen Warren } 190ae58d1e4SStephen Warren 1910b255e92SBill Pemberton static const struct of_device_id i2c_mux_pinctrl_of_match[] = { 192ae58d1e4SStephen Warren { .compatible = "i2c-mux-pinctrl", }, 193ae58d1e4SStephen Warren {}, 194ae58d1e4SStephen Warren }; 195ae58d1e4SStephen Warren MODULE_DEVICE_TABLE(of, i2c_mux_pinctrl_of_match); 196ae58d1e4SStephen Warren 197ae58d1e4SStephen Warren static struct platform_driver i2c_mux_pinctrl_driver = { 198ae58d1e4SStephen Warren .driver = { 199ae58d1e4SStephen Warren .name = "i2c-mux-pinctrl", 200ae58d1e4SStephen Warren .of_match_table = of_match_ptr(i2c_mux_pinctrl_of_match), 201ae58d1e4SStephen Warren }, 202ae58d1e4SStephen Warren .probe = i2c_mux_pinctrl_probe, 2030b255e92SBill Pemberton .remove = i2c_mux_pinctrl_remove, 204ae58d1e4SStephen Warren }; 205ae58d1e4SStephen Warren module_platform_driver(i2c_mux_pinctrl_driver); 206ae58d1e4SStephen Warren 207ae58d1e4SStephen Warren MODULE_DESCRIPTION("pinctrl-based I2C multiplexer driver"); 208ae58d1e4SStephen Warren MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>"); 209ae58d1e4SStephen Warren MODULE_LICENSE("GPL v2"); 210ae58d1e4SStephen Warren MODULE_ALIAS("platform:i2c-mux-pinctrl"); 211