brintos

brintos / linux-shallow public Read only

0
0
Text · 3.5 KiB · 10d6330 Raw
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