166 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * General Purpose I2C multiplexer4 *5 * Copyright (C) 2017 Axentia Technologies AB6 *7 * Author: Peter Rosin <peda@axentia.se>8 */9 10#include <linux/i2c.h>11#include <linux/i2c-mux.h>12#include <linux/module.h>13#include <linux/mux/consumer.h>14#include <linux/of.h>15#include <linux/platform_device.h>16 17struct mux {18 struct mux_control *control;19 20 bool do_not_deselect;21};22 23static int i2c_mux_select(struct i2c_mux_core *muxc, u32 chan)24{25 struct mux *mux = i2c_mux_priv(muxc);26 int ret;27 28 ret = mux_control_select(mux->control, chan);29 mux->do_not_deselect = ret < 0;30 31 return ret;32}33 34static int i2c_mux_deselect(struct i2c_mux_core *muxc, u32 chan)35{36 struct mux *mux = i2c_mux_priv(muxc);37 38 if (mux->do_not_deselect)39 return 0;40 41 return mux_control_deselect(mux->control);42}43 44static struct i2c_adapter *mux_parent_adapter(struct device *dev)45{46 struct device_node *np = dev->of_node;47 struct device_node *parent_np;48 struct i2c_adapter *parent;49 50 parent_np = of_parse_phandle(np, "i2c-parent", 0);51 if (!parent_np) {52 dev_err(dev, "Cannot parse i2c-parent\n");53 return ERR_PTR(-ENODEV);54 }55 parent = of_get_i2c_adapter_by_node(parent_np);56 of_node_put(parent_np);57 if (!parent)58 return ERR_PTR(-EPROBE_DEFER);59 60 return parent;61}62 63static const struct of_device_id i2c_mux_of_match[] = {64 { .compatible = "i2c-mux", },65 {},66};67MODULE_DEVICE_TABLE(of, i2c_mux_of_match);68 69static int i2c_mux_probe(struct platform_device *pdev)70{71 struct device *dev = &pdev->dev;72 struct device_node *np = dev->of_node;73 struct device_node *child;74 struct i2c_mux_core *muxc;75 struct mux *mux;76 struct i2c_adapter *parent;77 int children;78 int ret;79 80 if (!np)81 return -ENODEV;82 83 mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL);84 if (!mux)85 return -ENOMEM;86 87 mux->control = devm_mux_control_get(dev, NULL);88 if (IS_ERR(mux->control))89 return dev_err_probe(dev, PTR_ERR(mux->control),90 "failed to get control-mux\n");91 92 parent = mux_parent_adapter(dev);93 if (IS_ERR(parent))94 return dev_err_probe(dev, PTR_ERR(parent),95 "failed to get i2c-parent adapter\n");96 97 children = of_get_child_count(np);98 99 muxc = i2c_mux_alloc(parent, dev, children, 0, 0,100 i2c_mux_select, i2c_mux_deselect);101 if (!muxc) {102 ret = -ENOMEM;103 goto err_parent;104 }105 muxc->priv = mux;106 107 platform_set_drvdata(pdev, muxc);108 109 muxc->mux_locked = of_property_read_bool(np, "mux-locked");110 111 for_each_child_of_node(np, child) {112 u32 chan;113 114 ret = of_property_read_u32(child, "reg", &chan);115 if (ret < 0) {116 dev_err(dev, "no reg property for node '%pOFn'\n",117 child);118 goto err_children;119 }120 121 if (chan >= mux_control_states(mux->control)) {122 dev_err(dev, "invalid reg %u\n", chan);123 ret = -EINVAL;124 goto err_children;125 }126 127 ret = i2c_mux_add_adapter(muxc, 0, chan);128 if (ret)129 goto err_children;130 }131 132 dev_info(dev, "%d-port mux on %s adapter\n", children, parent->name);133 134 return 0;135 136err_children:137 of_node_put(child);138 i2c_mux_del_adapters(muxc);139err_parent:140 i2c_put_adapter(parent);141 142 return ret;143}144 145static void i2c_mux_remove(struct platform_device *pdev)146{147 struct i2c_mux_core *muxc = platform_get_drvdata(pdev);148 149 i2c_mux_del_adapters(muxc);150 i2c_put_adapter(muxc->parent);151}152 153static struct platform_driver i2c_mux_driver = {154 .probe = i2c_mux_probe,155 .remove_new = i2c_mux_remove,156 .driver = {157 .name = "i2c-mux-gpmux",158 .of_match_table = i2c_mux_of_match,159 },160};161module_platform_driver(i2c_mux_driver);162 163MODULE_DESCRIPTION("General Purpose I2C multiplexer driver");164MODULE_AUTHOR("Peter Rosin <peda@axentia.se>");165MODULE_LICENSE("GPL v2");166