18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * I2C multiplexer using pinctrl API 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/i2c.h> 98c2ecf20Sopenharmony_ci#include <linux/i2c-mux.h> 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <linux/pinctrl/consumer.h> 128c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 138c2ecf20Sopenharmony_ci#include <linux/slab.h> 148c2ecf20Sopenharmony_ci#include <linux/of.h> 158c2ecf20Sopenharmony_ci#include "../../pinctrl/core.h" 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_cistruct i2c_mux_pinctrl { 188c2ecf20Sopenharmony_ci struct pinctrl *pinctrl; 198c2ecf20Sopenharmony_ci struct pinctrl_state *states[]; 208c2ecf20Sopenharmony_ci}; 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_cistatic int i2c_mux_pinctrl_select(struct i2c_mux_core *muxc, u32 chan) 238c2ecf20Sopenharmony_ci{ 248c2ecf20Sopenharmony_ci struct i2c_mux_pinctrl *mux = i2c_mux_priv(muxc); 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci return pinctrl_select_state(mux->pinctrl, mux->states[chan]); 278c2ecf20Sopenharmony_ci} 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistatic int i2c_mux_pinctrl_deselect(struct i2c_mux_core *muxc, u32 chan) 308c2ecf20Sopenharmony_ci{ 318c2ecf20Sopenharmony_ci return i2c_mux_pinctrl_select(muxc, muxc->num_adapters); 328c2ecf20Sopenharmony_ci} 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistatic struct i2c_adapter *i2c_mux_pinctrl_root_adapter( 358c2ecf20Sopenharmony_ci struct pinctrl_state *state) 368c2ecf20Sopenharmony_ci{ 378c2ecf20Sopenharmony_ci struct i2c_adapter *root = NULL; 388c2ecf20Sopenharmony_ci struct pinctrl_setting *setting; 398c2ecf20Sopenharmony_ci struct i2c_adapter *pin_root; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci list_for_each_entry(setting, &state->settings, node) { 428c2ecf20Sopenharmony_ci pin_root = i2c_root_adapter(setting->pctldev->dev); 438c2ecf20Sopenharmony_ci if (!pin_root) 448c2ecf20Sopenharmony_ci return NULL; 458c2ecf20Sopenharmony_ci if (!root) 468c2ecf20Sopenharmony_ci root = pin_root; 478c2ecf20Sopenharmony_ci else if (root != pin_root) 488c2ecf20Sopenharmony_ci return NULL; 498c2ecf20Sopenharmony_ci } 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci return root; 528c2ecf20Sopenharmony_ci} 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistatic struct i2c_adapter *i2c_mux_pinctrl_parent_adapter(struct device *dev) 558c2ecf20Sopenharmony_ci{ 568c2ecf20Sopenharmony_ci struct device_node *np = dev->of_node; 578c2ecf20Sopenharmony_ci struct device_node *parent_np; 588c2ecf20Sopenharmony_ci struct i2c_adapter *parent; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci parent_np = of_parse_phandle(np, "i2c-parent", 0); 618c2ecf20Sopenharmony_ci if (!parent_np) { 628c2ecf20Sopenharmony_ci dev_err(dev, "Cannot parse i2c-parent\n"); 638c2ecf20Sopenharmony_ci return ERR_PTR(-ENODEV); 648c2ecf20Sopenharmony_ci } 658c2ecf20Sopenharmony_ci parent = of_get_i2c_adapter_by_node(parent_np); 668c2ecf20Sopenharmony_ci of_node_put(parent_np); 678c2ecf20Sopenharmony_ci if (!parent) 688c2ecf20Sopenharmony_ci return ERR_PTR(-EPROBE_DEFER); 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci return parent; 718c2ecf20Sopenharmony_ci} 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic int i2c_mux_pinctrl_probe(struct platform_device *pdev) 748c2ecf20Sopenharmony_ci{ 758c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 768c2ecf20Sopenharmony_ci struct device_node *np = dev->of_node; 778c2ecf20Sopenharmony_ci struct i2c_mux_core *muxc; 788c2ecf20Sopenharmony_ci struct i2c_mux_pinctrl *mux; 798c2ecf20Sopenharmony_ci struct i2c_adapter *parent; 808c2ecf20Sopenharmony_ci struct i2c_adapter *root; 818c2ecf20Sopenharmony_ci int num_names, i, ret; 828c2ecf20Sopenharmony_ci const char *name; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci num_names = of_property_count_strings(np, "pinctrl-names"); 858c2ecf20Sopenharmony_ci if (num_names < 0) { 868c2ecf20Sopenharmony_ci dev_err(dev, "Cannot parse pinctrl-names: %d\n", 878c2ecf20Sopenharmony_ci num_names); 888c2ecf20Sopenharmony_ci return num_names; 898c2ecf20Sopenharmony_ci } 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci parent = i2c_mux_pinctrl_parent_adapter(dev); 928c2ecf20Sopenharmony_ci if (IS_ERR(parent)) 938c2ecf20Sopenharmony_ci return PTR_ERR(parent); 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci muxc = i2c_mux_alloc(parent, dev, num_names, 968c2ecf20Sopenharmony_ci struct_size(mux, states, num_names), 978c2ecf20Sopenharmony_ci 0, i2c_mux_pinctrl_select, NULL); 988c2ecf20Sopenharmony_ci if (!muxc) { 998c2ecf20Sopenharmony_ci ret = -ENOMEM; 1008c2ecf20Sopenharmony_ci goto err_put_parent; 1018c2ecf20Sopenharmony_ci } 1028c2ecf20Sopenharmony_ci mux = i2c_mux_priv(muxc); 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, muxc); 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci mux->pinctrl = devm_pinctrl_get(dev); 1078c2ecf20Sopenharmony_ci if (IS_ERR(mux->pinctrl)) { 1088c2ecf20Sopenharmony_ci ret = PTR_ERR(mux->pinctrl); 1098c2ecf20Sopenharmony_ci dev_err(dev, "Cannot get pinctrl: %d\n", ret); 1108c2ecf20Sopenharmony_ci goto err_put_parent; 1118c2ecf20Sopenharmony_ci } 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci for (i = 0; i < num_names; i++) { 1148c2ecf20Sopenharmony_ci ret = of_property_read_string_index(np, "pinctrl-names", i, 1158c2ecf20Sopenharmony_ci &name); 1168c2ecf20Sopenharmony_ci if (ret < 0) { 1178c2ecf20Sopenharmony_ci dev_err(dev, "Cannot parse pinctrl-names: %d\n", ret); 1188c2ecf20Sopenharmony_ci goto err_put_parent; 1198c2ecf20Sopenharmony_ci } 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci mux->states[i] = pinctrl_lookup_state(mux->pinctrl, name); 1228c2ecf20Sopenharmony_ci if (IS_ERR(mux->states[i])) { 1238c2ecf20Sopenharmony_ci ret = PTR_ERR(mux->states[i]); 1248c2ecf20Sopenharmony_ci dev_err(dev, "Cannot look up pinctrl state %s: %d\n", 1258c2ecf20Sopenharmony_ci name, ret); 1268c2ecf20Sopenharmony_ci goto err_put_parent; 1278c2ecf20Sopenharmony_ci } 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci if (strcmp(name, "idle")) 1308c2ecf20Sopenharmony_ci continue; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci if (i != num_names - 1) { 1338c2ecf20Sopenharmony_ci dev_err(dev, "idle state must be last\n"); 1348c2ecf20Sopenharmony_ci ret = -EINVAL; 1358c2ecf20Sopenharmony_ci goto err_put_parent; 1368c2ecf20Sopenharmony_ci } 1378c2ecf20Sopenharmony_ci muxc->deselect = i2c_mux_pinctrl_deselect; 1388c2ecf20Sopenharmony_ci } 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci root = i2c_root_adapter(&muxc->parent->dev); 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci muxc->mux_locked = true; 1438c2ecf20Sopenharmony_ci for (i = 0; i < num_names; i++) { 1448c2ecf20Sopenharmony_ci if (root != i2c_mux_pinctrl_root_adapter(mux->states[i])) { 1458c2ecf20Sopenharmony_ci muxc->mux_locked = false; 1468c2ecf20Sopenharmony_ci break; 1478c2ecf20Sopenharmony_ci } 1488c2ecf20Sopenharmony_ci } 1498c2ecf20Sopenharmony_ci if (muxc->mux_locked) 1508c2ecf20Sopenharmony_ci dev_info(dev, "mux-locked i2c mux\n"); 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci /* Do not add any adapter for the idle state (if it's there at all). */ 1538c2ecf20Sopenharmony_ci for (i = 0; i < num_names - !!muxc->deselect; i++) { 1548c2ecf20Sopenharmony_ci ret = i2c_mux_add_adapter(muxc, 0, i, 0); 1558c2ecf20Sopenharmony_ci if (ret) 1568c2ecf20Sopenharmony_ci goto err_del_adapter; 1578c2ecf20Sopenharmony_ci } 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci return 0; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_cierr_del_adapter: 1628c2ecf20Sopenharmony_ci i2c_mux_del_adapters(muxc); 1638c2ecf20Sopenharmony_cierr_put_parent: 1648c2ecf20Sopenharmony_ci i2c_put_adapter(parent); 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci return ret; 1678c2ecf20Sopenharmony_ci} 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_cistatic int i2c_mux_pinctrl_remove(struct platform_device *pdev) 1708c2ecf20Sopenharmony_ci{ 1718c2ecf20Sopenharmony_ci struct i2c_mux_core *muxc = platform_get_drvdata(pdev); 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci i2c_mux_del_adapters(muxc); 1748c2ecf20Sopenharmony_ci i2c_put_adapter(muxc->parent); 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci return 0; 1778c2ecf20Sopenharmony_ci} 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_cistatic const struct of_device_id i2c_mux_pinctrl_of_match[] = { 1808c2ecf20Sopenharmony_ci { .compatible = "i2c-mux-pinctrl", }, 1818c2ecf20Sopenharmony_ci {}, 1828c2ecf20Sopenharmony_ci}; 1838c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, i2c_mux_pinctrl_of_match); 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_cistatic struct platform_driver i2c_mux_pinctrl_driver = { 1868c2ecf20Sopenharmony_ci .driver = { 1878c2ecf20Sopenharmony_ci .name = "i2c-mux-pinctrl", 1888c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(i2c_mux_pinctrl_of_match), 1898c2ecf20Sopenharmony_ci }, 1908c2ecf20Sopenharmony_ci .probe = i2c_mux_pinctrl_probe, 1918c2ecf20Sopenharmony_ci .remove = i2c_mux_pinctrl_remove, 1928c2ecf20Sopenharmony_ci}; 1938c2ecf20Sopenharmony_cimodule_platform_driver(i2c_mux_pinctrl_driver); 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("pinctrl-based I2C multiplexer driver"); 1968c2ecf20Sopenharmony_ciMODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>"); 1978c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 1988c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:i2c-mux-pinctrl"); 199