cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

i2c-mux-gpmux.c (3586B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * General Purpose I2C multiplexer
      4 *
      5 * Copyright (C) 2017 Axentia Technologies AB
      6 *
      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_device.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_find_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, 0);
    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	i2c_mux_del_adapters(muxc);
    138err_parent:
    139	i2c_put_adapter(parent);
    140
    141	return ret;
    142}
    143
    144static int i2c_mux_remove(struct platform_device *pdev)
    145{
    146	struct i2c_mux_core *muxc = platform_get_drvdata(pdev);
    147
    148	i2c_mux_del_adapters(muxc);
    149	i2c_put_adapter(muxc->parent);
    150
    151	return 0;
    152}
    153
    154static struct platform_driver i2c_mux_driver = {
    155	.probe	= i2c_mux_probe,
    156	.remove	= i2c_mux_remove,
    157	.driver	= {
    158		.name	= "i2c-mux-gpmux",
    159		.of_match_table = i2c_mux_of_match,
    160	},
    161};
    162module_platform_driver(i2c_mux_driver);
    163
    164MODULE_DESCRIPTION("General Purpose I2C multiplexer driver");
    165MODULE_AUTHOR("Peter Rosin <peda@axentia.se>");
    166MODULE_LICENSE("GPL v2");