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/i2c-mux-pinctrl.h> 24ae58d1e4SStephen Warren #include <linux/platform_device.h> 25ae58d1e4SStephen Warren #include <linux/slab.h> 264edd65e6SSachin Kamat #include <linux/of.h> 27*6ef91fccSPeter Rosin #include "../../pinctrl/core.h" 28ae58d1e4SStephen Warren 29ae58d1e4SStephen Warren struct i2c_mux_pinctrl { 30ae58d1e4SStephen Warren struct i2c_mux_pinctrl_platform_data *pdata; 31ae58d1e4SStephen Warren struct pinctrl *pinctrl; 32ae58d1e4SStephen Warren struct pinctrl_state **states; 33ae58d1e4SStephen Warren struct pinctrl_state *state_idle; 34ae58d1e4SStephen Warren }; 35ae58d1e4SStephen Warren 364bbe7fb0SPeter Rosin static int i2c_mux_pinctrl_select(struct i2c_mux_core *muxc, u32 chan) 37ae58d1e4SStephen Warren { 384bbe7fb0SPeter Rosin struct i2c_mux_pinctrl *mux = i2c_mux_priv(muxc); 39ae58d1e4SStephen Warren 40ae58d1e4SStephen Warren return pinctrl_select_state(mux->pinctrl, mux->states[chan]); 41ae58d1e4SStephen Warren } 42ae58d1e4SStephen Warren 434bbe7fb0SPeter Rosin static int i2c_mux_pinctrl_deselect(struct i2c_mux_core *muxc, u32 chan) 44ae58d1e4SStephen Warren { 454bbe7fb0SPeter Rosin struct i2c_mux_pinctrl *mux = i2c_mux_priv(muxc); 46ae58d1e4SStephen Warren 47ae58d1e4SStephen Warren return pinctrl_select_state(mux->pinctrl, mux->state_idle); 48ae58d1e4SStephen Warren } 49ae58d1e4SStephen Warren 50ae58d1e4SStephen Warren #ifdef CONFIG_OF 51ae58d1e4SStephen Warren static int i2c_mux_pinctrl_parse_dt(struct i2c_mux_pinctrl *mux, 52ae58d1e4SStephen Warren struct platform_device *pdev) 53ae58d1e4SStephen Warren { 54ae58d1e4SStephen Warren struct device_node *np = pdev->dev.of_node; 55ae58d1e4SStephen Warren int num_names, i, ret; 56ae58d1e4SStephen Warren struct device_node *adapter_np; 57ae58d1e4SStephen Warren struct i2c_adapter *adapter; 58ae58d1e4SStephen Warren 59ae58d1e4SStephen Warren if (!np) 60ae58d1e4SStephen Warren return 0; 61ae58d1e4SStephen Warren 62ae58d1e4SStephen Warren mux->pdata = devm_kzalloc(&pdev->dev, sizeof(*mux->pdata), GFP_KERNEL); 634bbe7fb0SPeter Rosin if (!mux->pdata) 64ae58d1e4SStephen Warren return -ENOMEM; 65ae58d1e4SStephen Warren 66ae58d1e4SStephen Warren num_names = of_property_count_strings(np, "pinctrl-names"); 67ae58d1e4SStephen Warren if (num_names < 0) { 684bbe7fb0SPeter Rosin dev_err(&pdev->dev, "Cannot parse pinctrl-names: %d\n", 69ae58d1e4SStephen Warren num_names); 70ae58d1e4SStephen Warren return num_names; 71ae58d1e4SStephen Warren } 72ae58d1e4SStephen Warren 73ae58d1e4SStephen Warren mux->pdata->pinctrl_states = devm_kzalloc(&pdev->dev, 74ae58d1e4SStephen Warren sizeof(*mux->pdata->pinctrl_states) * num_names, 75ae58d1e4SStephen Warren GFP_KERNEL); 764bbe7fb0SPeter Rosin if (!mux->pdata->pinctrl_states) 77ae58d1e4SStephen Warren return -ENOMEM; 78ae58d1e4SStephen Warren 79ae58d1e4SStephen Warren for (i = 0; i < num_names; i++) { 80ae58d1e4SStephen Warren ret = of_property_read_string_index(np, "pinctrl-names", i, 81ae58d1e4SStephen Warren &mux->pdata->pinctrl_states[mux->pdata->bus_count]); 82ae58d1e4SStephen Warren if (ret < 0) { 834bbe7fb0SPeter Rosin dev_err(&pdev->dev, "Cannot parse pinctrl-names: %d\n", 84ae58d1e4SStephen Warren ret); 85ae58d1e4SStephen Warren return ret; 86ae58d1e4SStephen Warren } 87ae58d1e4SStephen Warren if (!strcmp(mux->pdata->pinctrl_states[mux->pdata->bus_count], 88ae58d1e4SStephen Warren "idle")) { 89ae58d1e4SStephen Warren if (i != num_names - 1) { 904bbe7fb0SPeter Rosin dev_err(&pdev->dev, 914bbe7fb0SPeter Rosin "idle state must be last\n"); 92ae58d1e4SStephen Warren return -EINVAL; 93ae58d1e4SStephen Warren } 94ae58d1e4SStephen Warren mux->pdata->pinctrl_state_idle = "idle"; 95ae58d1e4SStephen Warren } else { 96ae58d1e4SStephen Warren mux->pdata->bus_count++; 97ae58d1e4SStephen Warren } 98ae58d1e4SStephen Warren } 99ae58d1e4SStephen Warren 100ae58d1e4SStephen Warren adapter_np = of_parse_phandle(np, "i2c-parent", 0); 101ae58d1e4SStephen Warren if (!adapter_np) { 1024bbe7fb0SPeter Rosin dev_err(&pdev->dev, "Cannot parse i2c-parent\n"); 103ae58d1e4SStephen Warren return -ENODEV; 104ae58d1e4SStephen Warren } 105ae58d1e4SStephen Warren adapter = of_find_i2c_adapter_by_node(adapter_np); 106bdbf4a29SVladimir Zapolskiy of_node_put(adapter_np); 107ae58d1e4SStephen Warren if (!adapter) { 1084bbe7fb0SPeter Rosin dev_err(&pdev->dev, "Cannot find parent bus\n"); 1092737de46SWolfram Sang return -EPROBE_DEFER; 110ae58d1e4SStephen Warren } 111ae58d1e4SStephen Warren mux->pdata->parent_bus_num = i2c_adapter_id(adapter); 112ae58d1e4SStephen Warren put_device(&adapter->dev); 113ae58d1e4SStephen Warren 114ae58d1e4SStephen Warren return 0; 115ae58d1e4SStephen Warren } 116ae58d1e4SStephen Warren #else 117ae58d1e4SStephen Warren static inline int i2c_mux_pinctrl_parse_dt(struct i2c_mux_pinctrl *mux, 118ae58d1e4SStephen Warren struct platform_device *pdev) 119ae58d1e4SStephen Warren { 120ae58d1e4SStephen Warren return 0; 121ae58d1e4SStephen Warren } 122ae58d1e4SStephen Warren #endif 123ae58d1e4SStephen Warren 124*6ef91fccSPeter Rosin static struct i2c_adapter *i2c_mux_pinctrl_root_adapter( 125*6ef91fccSPeter Rosin struct pinctrl_state *state) 126*6ef91fccSPeter Rosin { 127*6ef91fccSPeter Rosin struct i2c_adapter *root = NULL; 128*6ef91fccSPeter Rosin struct pinctrl_setting *setting; 129*6ef91fccSPeter Rosin struct i2c_adapter *pin_root; 130*6ef91fccSPeter Rosin 131*6ef91fccSPeter Rosin list_for_each_entry(setting, &state->settings, node) { 132*6ef91fccSPeter Rosin pin_root = i2c_root_adapter(setting->pctldev->dev); 133*6ef91fccSPeter Rosin if (!pin_root) 134*6ef91fccSPeter Rosin return NULL; 135*6ef91fccSPeter Rosin if (!root) 136*6ef91fccSPeter Rosin root = pin_root; 137*6ef91fccSPeter Rosin else if (root != pin_root) 138*6ef91fccSPeter Rosin return NULL; 139*6ef91fccSPeter Rosin } 140*6ef91fccSPeter Rosin 141*6ef91fccSPeter Rosin return root; 142*6ef91fccSPeter Rosin } 143*6ef91fccSPeter Rosin 1440b255e92SBill Pemberton static int i2c_mux_pinctrl_probe(struct platform_device *pdev) 145ae58d1e4SStephen Warren { 1464bbe7fb0SPeter Rosin struct i2c_mux_core *muxc; 147ae58d1e4SStephen Warren struct i2c_mux_pinctrl *mux; 148*6ef91fccSPeter Rosin struct i2c_adapter *root; 149ae58d1e4SStephen Warren int i, ret; 150ae58d1e4SStephen Warren 151ae58d1e4SStephen Warren mux = devm_kzalloc(&pdev->dev, sizeof(*mux), GFP_KERNEL); 152ae58d1e4SStephen Warren if (!mux) { 153ae58d1e4SStephen Warren ret = -ENOMEM; 154ae58d1e4SStephen Warren goto err; 155ae58d1e4SStephen Warren } 156ae58d1e4SStephen Warren 1576d4028c6SJingoo Han mux->pdata = dev_get_platdata(&pdev->dev); 158ae58d1e4SStephen Warren if (!mux->pdata) { 159ae58d1e4SStephen Warren ret = i2c_mux_pinctrl_parse_dt(mux, pdev); 160ae58d1e4SStephen Warren if (ret < 0) 161ae58d1e4SStephen Warren goto err; 162ae58d1e4SStephen Warren } 163ae58d1e4SStephen Warren if (!mux->pdata) { 164ae58d1e4SStephen Warren dev_err(&pdev->dev, "Missing platform data\n"); 165ae58d1e4SStephen Warren ret = -ENODEV; 166ae58d1e4SStephen Warren goto err; 167ae58d1e4SStephen Warren } 168ae58d1e4SStephen Warren 169ae58d1e4SStephen Warren mux->states = devm_kzalloc(&pdev->dev, 170ae58d1e4SStephen Warren sizeof(*mux->states) * mux->pdata->bus_count, 171ae58d1e4SStephen Warren GFP_KERNEL); 172ae58d1e4SStephen Warren if (!mux->states) { 173ae58d1e4SStephen Warren dev_err(&pdev->dev, "Cannot allocate states\n"); 174ae58d1e4SStephen Warren ret = -ENOMEM; 175ae58d1e4SStephen Warren goto err; 176ae58d1e4SStephen Warren } 177ae58d1e4SStephen Warren 1784bbe7fb0SPeter Rosin muxc = i2c_mux_alloc(NULL, &pdev->dev, mux->pdata->bus_count, 0, 0, 1794bbe7fb0SPeter Rosin i2c_mux_pinctrl_select, NULL); 1804bbe7fb0SPeter Rosin if (!muxc) { 181ae58d1e4SStephen Warren ret = -ENOMEM; 182ae58d1e4SStephen Warren goto err; 183ae58d1e4SStephen Warren } 1844bbe7fb0SPeter Rosin muxc->priv = mux; 1854bbe7fb0SPeter Rosin 1864bbe7fb0SPeter Rosin platform_set_drvdata(pdev, muxc); 187ae58d1e4SStephen Warren 188ae58d1e4SStephen Warren mux->pinctrl = devm_pinctrl_get(&pdev->dev); 189ae58d1e4SStephen Warren if (IS_ERR(mux->pinctrl)) { 190ae58d1e4SStephen Warren ret = PTR_ERR(mux->pinctrl); 191ae58d1e4SStephen Warren dev_err(&pdev->dev, "Cannot get pinctrl: %d\n", ret); 192ae58d1e4SStephen Warren goto err; 193ae58d1e4SStephen Warren } 194ae58d1e4SStephen Warren for (i = 0; i < mux->pdata->bus_count; i++) { 195ae58d1e4SStephen Warren mux->states[i] = pinctrl_lookup_state(mux->pinctrl, 196ae58d1e4SStephen Warren mux->pdata->pinctrl_states[i]); 197ae58d1e4SStephen Warren if (IS_ERR(mux->states[i])) { 198ae58d1e4SStephen Warren ret = PTR_ERR(mux->states[i]); 199ae58d1e4SStephen Warren dev_err(&pdev->dev, 200ae58d1e4SStephen Warren "Cannot look up pinctrl state %s: %d\n", 201ae58d1e4SStephen Warren mux->pdata->pinctrl_states[i], ret); 202ae58d1e4SStephen Warren goto err; 203ae58d1e4SStephen Warren } 204ae58d1e4SStephen Warren } 205ae58d1e4SStephen Warren if (mux->pdata->pinctrl_state_idle) { 206ae58d1e4SStephen Warren mux->state_idle = pinctrl_lookup_state(mux->pinctrl, 207ae58d1e4SStephen Warren mux->pdata->pinctrl_state_idle); 208ae58d1e4SStephen Warren if (IS_ERR(mux->state_idle)) { 209ae58d1e4SStephen Warren ret = PTR_ERR(mux->state_idle); 210ae58d1e4SStephen Warren dev_err(&pdev->dev, 211ae58d1e4SStephen Warren "Cannot look up pinctrl state %s: %d\n", 212ae58d1e4SStephen Warren mux->pdata->pinctrl_state_idle, ret); 213ae58d1e4SStephen Warren goto err; 214ae58d1e4SStephen Warren } 215ae58d1e4SStephen Warren 2164bbe7fb0SPeter Rosin muxc->deselect = i2c_mux_pinctrl_deselect; 217ae58d1e4SStephen Warren } 218ae58d1e4SStephen Warren 2194bbe7fb0SPeter Rosin muxc->parent = i2c_get_adapter(mux->pdata->parent_bus_num); 2204bbe7fb0SPeter Rosin if (!muxc->parent) { 221ae58d1e4SStephen Warren dev_err(&pdev->dev, "Parent adapter (%d) not found\n", 222ae58d1e4SStephen Warren mux->pdata->parent_bus_num); 2232737de46SWolfram Sang ret = -EPROBE_DEFER; 224ae58d1e4SStephen Warren goto err; 225ae58d1e4SStephen Warren } 226ae58d1e4SStephen Warren 227*6ef91fccSPeter Rosin root = i2c_root_adapter(&muxc->parent->dev); 228*6ef91fccSPeter Rosin 229*6ef91fccSPeter Rosin muxc->mux_locked = true; 230*6ef91fccSPeter Rosin for (i = 0; i < mux->pdata->bus_count; i++) { 231*6ef91fccSPeter Rosin if (root != i2c_mux_pinctrl_root_adapter(mux->states[i])) { 232*6ef91fccSPeter Rosin muxc->mux_locked = false; 233*6ef91fccSPeter Rosin break; 234*6ef91fccSPeter Rosin } 235*6ef91fccSPeter Rosin } 236*6ef91fccSPeter Rosin if (muxc->mux_locked && mux->pdata->pinctrl_state_idle && 237*6ef91fccSPeter Rosin root != i2c_mux_pinctrl_root_adapter(mux->state_idle)) 238*6ef91fccSPeter Rosin muxc->mux_locked = false; 239*6ef91fccSPeter Rosin 240*6ef91fccSPeter Rosin if (muxc->mux_locked) 241*6ef91fccSPeter Rosin dev_info(&pdev->dev, "mux-locked i2c mux\n"); 242*6ef91fccSPeter Rosin 243ae58d1e4SStephen Warren for (i = 0; i < mux->pdata->bus_count; i++) { 244ae58d1e4SStephen Warren u32 bus = mux->pdata->base_bus_num ? 245ae58d1e4SStephen Warren (mux->pdata->base_bus_num + i) : 0; 246ae58d1e4SStephen Warren 2474bbe7fb0SPeter Rosin ret = i2c_mux_add_adapter(muxc, bus, i, 0); 2484bbe7fb0SPeter Rosin if (ret) { 249ae58d1e4SStephen Warren dev_err(&pdev->dev, "Failed to add adapter %d\n", i); 250ae58d1e4SStephen Warren goto err_del_adapter; 251ae58d1e4SStephen Warren } 252ae58d1e4SStephen Warren } 253ae58d1e4SStephen Warren 254ae58d1e4SStephen Warren return 0; 255ae58d1e4SStephen Warren 256ae58d1e4SStephen Warren err_del_adapter: 2574bbe7fb0SPeter Rosin i2c_mux_del_adapters(muxc); 2584bbe7fb0SPeter Rosin i2c_put_adapter(muxc->parent); 259ae58d1e4SStephen Warren err: 260ae58d1e4SStephen Warren return ret; 261ae58d1e4SStephen Warren } 262ae58d1e4SStephen Warren 2630b255e92SBill Pemberton static int i2c_mux_pinctrl_remove(struct platform_device *pdev) 264ae58d1e4SStephen Warren { 2654bbe7fb0SPeter Rosin struct i2c_mux_core *muxc = platform_get_drvdata(pdev); 266ae58d1e4SStephen Warren 2674bbe7fb0SPeter Rosin i2c_mux_del_adapters(muxc); 2684bbe7fb0SPeter Rosin i2c_put_adapter(muxc->parent); 269ae58d1e4SStephen Warren return 0; 270ae58d1e4SStephen Warren } 271ae58d1e4SStephen Warren 272ae58d1e4SStephen Warren #ifdef CONFIG_OF 2730b255e92SBill Pemberton static const struct of_device_id i2c_mux_pinctrl_of_match[] = { 274ae58d1e4SStephen Warren { .compatible = "i2c-mux-pinctrl", }, 275ae58d1e4SStephen Warren {}, 276ae58d1e4SStephen Warren }; 277ae58d1e4SStephen Warren MODULE_DEVICE_TABLE(of, i2c_mux_pinctrl_of_match); 278ae58d1e4SStephen Warren #endif 279ae58d1e4SStephen Warren 280ae58d1e4SStephen Warren static struct platform_driver i2c_mux_pinctrl_driver = { 281ae58d1e4SStephen Warren .driver = { 282ae58d1e4SStephen Warren .name = "i2c-mux-pinctrl", 283ae58d1e4SStephen Warren .of_match_table = of_match_ptr(i2c_mux_pinctrl_of_match), 284ae58d1e4SStephen Warren }, 285ae58d1e4SStephen Warren .probe = i2c_mux_pinctrl_probe, 2860b255e92SBill Pemberton .remove = i2c_mux_pinctrl_remove, 287ae58d1e4SStephen Warren }; 288ae58d1e4SStephen Warren module_platform_driver(i2c_mux_pinctrl_driver); 289ae58d1e4SStephen Warren 290ae58d1e4SStephen Warren MODULE_DESCRIPTION("pinctrl-based I2C multiplexer driver"); 291ae58d1e4SStephen Warren MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>"); 292ae58d1e4SStephen Warren MODULE_LICENSE("GPL v2"); 293ae58d1e4SStephen Warren MODULE_ALIAS("platform:i2c-mux-pinctrl"); 294