18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * General Purpose I2C multiplexer 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2017 Axentia Technologies AB 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Author: Peter Rosin <peda@axentia.se> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/i2c.h> 118c2ecf20Sopenharmony_ci#include <linux/i2c-mux.h> 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/mux/consumer.h> 148c2ecf20Sopenharmony_ci#include <linux/of_device.h> 158c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_cistruct mux { 188c2ecf20Sopenharmony_ci struct mux_control *control; 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci bool do_not_deselect; 218c2ecf20Sopenharmony_ci}; 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_cistatic int i2c_mux_select(struct i2c_mux_core *muxc, u32 chan) 248c2ecf20Sopenharmony_ci{ 258c2ecf20Sopenharmony_ci struct mux *mux = i2c_mux_priv(muxc); 268c2ecf20Sopenharmony_ci int ret; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci ret = mux_control_select(mux->control, chan); 298c2ecf20Sopenharmony_ci mux->do_not_deselect = ret < 0; 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci return ret; 328c2ecf20Sopenharmony_ci} 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistatic int i2c_mux_deselect(struct i2c_mux_core *muxc, u32 chan) 358c2ecf20Sopenharmony_ci{ 368c2ecf20Sopenharmony_ci struct mux *mux = i2c_mux_priv(muxc); 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci if (mux->do_not_deselect) 398c2ecf20Sopenharmony_ci return 0; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci return mux_control_deselect(mux->control); 428c2ecf20Sopenharmony_ci} 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistatic struct i2c_adapter *mux_parent_adapter(struct device *dev) 458c2ecf20Sopenharmony_ci{ 468c2ecf20Sopenharmony_ci struct device_node *np = dev->of_node; 478c2ecf20Sopenharmony_ci struct device_node *parent_np; 488c2ecf20Sopenharmony_ci struct i2c_adapter *parent; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci parent_np = of_parse_phandle(np, "i2c-parent", 0); 518c2ecf20Sopenharmony_ci if (!parent_np) { 528c2ecf20Sopenharmony_ci dev_err(dev, "Cannot parse i2c-parent\n"); 538c2ecf20Sopenharmony_ci return ERR_PTR(-ENODEV); 548c2ecf20Sopenharmony_ci } 558c2ecf20Sopenharmony_ci parent = of_get_i2c_adapter_by_node(parent_np); 568c2ecf20Sopenharmony_ci of_node_put(parent_np); 578c2ecf20Sopenharmony_ci if (!parent) 588c2ecf20Sopenharmony_ci return ERR_PTR(-EPROBE_DEFER); 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci return parent; 618c2ecf20Sopenharmony_ci} 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cistatic const struct of_device_id i2c_mux_of_match[] = { 648c2ecf20Sopenharmony_ci { .compatible = "i2c-mux", }, 658c2ecf20Sopenharmony_ci {}, 668c2ecf20Sopenharmony_ci}; 678c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, i2c_mux_of_match); 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_cistatic int i2c_mux_probe(struct platform_device *pdev) 708c2ecf20Sopenharmony_ci{ 718c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 728c2ecf20Sopenharmony_ci struct device_node *np = dev->of_node; 738c2ecf20Sopenharmony_ci struct device_node *child; 748c2ecf20Sopenharmony_ci struct i2c_mux_core *muxc; 758c2ecf20Sopenharmony_ci struct mux *mux; 768c2ecf20Sopenharmony_ci struct i2c_adapter *parent; 778c2ecf20Sopenharmony_ci int children; 788c2ecf20Sopenharmony_ci int ret; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci if (!np) 818c2ecf20Sopenharmony_ci return -ENODEV; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL); 848c2ecf20Sopenharmony_ci if (!mux) 858c2ecf20Sopenharmony_ci return -ENOMEM; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci mux->control = devm_mux_control_get(dev, NULL); 888c2ecf20Sopenharmony_ci if (IS_ERR(mux->control)) 898c2ecf20Sopenharmony_ci return dev_err_probe(dev, PTR_ERR(mux->control), 908c2ecf20Sopenharmony_ci "failed to get control-mux\n"); 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci parent = mux_parent_adapter(dev); 938c2ecf20Sopenharmony_ci if (IS_ERR(parent)) 948c2ecf20Sopenharmony_ci return dev_err_probe(dev, PTR_ERR(parent), 958c2ecf20Sopenharmony_ci "failed to get i2c-parent adapter\n"); 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci children = of_get_child_count(np); 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci muxc = i2c_mux_alloc(parent, dev, children, 0, 0, 1008c2ecf20Sopenharmony_ci i2c_mux_select, i2c_mux_deselect); 1018c2ecf20Sopenharmony_ci if (!muxc) { 1028c2ecf20Sopenharmony_ci ret = -ENOMEM; 1038c2ecf20Sopenharmony_ci goto err_parent; 1048c2ecf20Sopenharmony_ci } 1058c2ecf20Sopenharmony_ci muxc->priv = mux; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, muxc); 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci muxc->mux_locked = of_property_read_bool(np, "mux-locked"); 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci for_each_child_of_node(np, child) { 1128c2ecf20Sopenharmony_ci u32 chan; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci ret = of_property_read_u32(child, "reg", &chan); 1158c2ecf20Sopenharmony_ci if (ret < 0) { 1168c2ecf20Sopenharmony_ci dev_err(dev, "no reg property for node '%pOFn'\n", 1178c2ecf20Sopenharmony_ci child); 1188c2ecf20Sopenharmony_ci goto err_children; 1198c2ecf20Sopenharmony_ci } 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci if (chan >= mux_control_states(mux->control)) { 1228c2ecf20Sopenharmony_ci dev_err(dev, "invalid reg %u\n", chan); 1238c2ecf20Sopenharmony_ci ret = -EINVAL; 1248c2ecf20Sopenharmony_ci goto err_children; 1258c2ecf20Sopenharmony_ci } 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci ret = i2c_mux_add_adapter(muxc, 0, chan, 0); 1288c2ecf20Sopenharmony_ci if (ret) 1298c2ecf20Sopenharmony_ci goto err_children; 1308c2ecf20Sopenharmony_ci } 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci dev_info(dev, "%d-port mux on %s adapter\n", children, parent->name); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci return 0; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_cierr_children: 1378c2ecf20Sopenharmony_ci of_node_put(child); 1388c2ecf20Sopenharmony_ci i2c_mux_del_adapters(muxc); 1398c2ecf20Sopenharmony_cierr_parent: 1408c2ecf20Sopenharmony_ci i2c_put_adapter(parent); 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci return ret; 1438c2ecf20Sopenharmony_ci} 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_cistatic int i2c_mux_remove(struct platform_device *pdev) 1468c2ecf20Sopenharmony_ci{ 1478c2ecf20Sopenharmony_ci struct i2c_mux_core *muxc = platform_get_drvdata(pdev); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci i2c_mux_del_adapters(muxc); 1508c2ecf20Sopenharmony_ci i2c_put_adapter(muxc->parent); 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci return 0; 1538c2ecf20Sopenharmony_ci} 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_cistatic struct platform_driver i2c_mux_driver = { 1568c2ecf20Sopenharmony_ci .probe = i2c_mux_probe, 1578c2ecf20Sopenharmony_ci .remove = i2c_mux_remove, 1588c2ecf20Sopenharmony_ci .driver = { 1598c2ecf20Sopenharmony_ci .name = "i2c-mux-gpmux", 1608c2ecf20Sopenharmony_ci .of_match_table = i2c_mux_of_match, 1618c2ecf20Sopenharmony_ci }, 1628c2ecf20Sopenharmony_ci}; 1638c2ecf20Sopenharmony_cimodule_platform_driver(i2c_mux_driver); 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("General Purpose I2C multiplexer driver"); 1668c2ecf20Sopenharmony_ciMODULE_AUTHOR("Peter Rosin <peda@axentia.se>"); 1678c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 168