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-demux-pinctrl.c (8469B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Pinctrl based I2C DeMultiplexer
      4 *
      5 * Copyright (C) 2015-16 by Wolfram Sang, Sang Engineering <wsa@sang-engineering.com>
      6 * Copyright (C) 2015-16 by Renesas Electronics Corporation
      7 *
      8 * See the bindings doc for DTS setup and the sysfs doc for usage information.
      9 * (look for filenames containing 'i2c-demux-pinctrl' in Documentation/)
     10 */
     11
     12#include <linux/i2c.h>
     13#include <linux/init.h>
     14#include <linux/module.h>
     15#include <linux/of.h>
     16#include <linux/pinctrl/consumer.h>
     17#include <linux/platform_device.h>
     18#include <linux/pm_runtime.h>
     19#include <linux/slab.h>
     20#include <linux/sysfs.h>
     21
     22struct i2c_demux_pinctrl_chan {
     23	struct device_node *parent_np;
     24	struct i2c_adapter *parent_adap;
     25	struct of_changeset chgset;
     26};
     27
     28struct i2c_demux_pinctrl_priv {
     29	int cur_chan;
     30	int num_chan;
     31	struct device *dev;
     32	const char *bus_name;
     33	struct i2c_adapter cur_adap;
     34	struct i2c_algorithm algo;
     35	struct i2c_demux_pinctrl_chan chan[];
     36};
     37
     38static int i2c_demux_master_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
     39{
     40	struct i2c_demux_pinctrl_priv *priv = adap->algo_data;
     41	struct i2c_adapter *parent = priv->chan[priv->cur_chan].parent_adap;
     42
     43	return __i2c_transfer(parent, msgs, num);
     44}
     45
     46static u32 i2c_demux_functionality(struct i2c_adapter *adap)
     47{
     48	struct i2c_demux_pinctrl_priv *priv = adap->algo_data;
     49	struct i2c_adapter *parent = priv->chan[priv->cur_chan].parent_adap;
     50
     51	return parent->algo->functionality(parent);
     52}
     53
     54static int i2c_demux_activate_master(struct i2c_demux_pinctrl_priv *priv, u32 new_chan)
     55{
     56	struct i2c_adapter *adap;
     57	struct pinctrl *p;
     58	int ret;
     59
     60	ret = of_changeset_apply(&priv->chan[new_chan].chgset);
     61	if (ret)
     62		goto err;
     63
     64	adap = of_find_i2c_adapter_by_node(priv->chan[new_chan].parent_np);
     65	if (!adap) {
     66		ret = -ENODEV;
     67		goto err_with_revert;
     68	}
     69
     70	/*
     71	 * Check if there are pinctrl states at all. Note: we cant' use
     72	 * devm_pinctrl_get_select() because we need to distinguish between
     73	 * the -ENODEV from devm_pinctrl_get() and pinctrl_lookup_state().
     74	 */
     75	p = devm_pinctrl_get(adap->dev.parent);
     76	if (IS_ERR(p)) {
     77		ret = PTR_ERR(p);
     78		/* continue if just no pinctrl states (e.g. i2c-gpio), otherwise exit */
     79		if (ret != -ENODEV)
     80			goto err_with_put;
     81	} else {
     82		/* there are states. check and use them */
     83		struct pinctrl_state *s = pinctrl_lookup_state(p, priv->bus_name);
     84
     85		if (IS_ERR(s)) {
     86			ret = PTR_ERR(s);
     87			goto err_with_put;
     88		}
     89		ret = pinctrl_select_state(p, s);
     90		if (ret < 0)
     91			goto err_with_put;
     92	}
     93
     94	priv->chan[new_chan].parent_adap = adap;
     95	priv->cur_chan = new_chan;
     96
     97	/* Now fill out current adapter structure. cur_chan must be up to date */
     98	priv->algo.master_xfer = i2c_demux_master_xfer;
     99	if (adap->algo->master_xfer_atomic)
    100		priv->algo.master_xfer_atomic = i2c_demux_master_xfer;
    101	priv->algo.functionality = i2c_demux_functionality;
    102
    103	snprintf(priv->cur_adap.name, sizeof(priv->cur_adap.name),
    104		 "i2c-demux (master i2c-%d)", i2c_adapter_id(adap));
    105	priv->cur_adap.owner = THIS_MODULE;
    106	priv->cur_adap.algo = &priv->algo;
    107	priv->cur_adap.algo_data = priv;
    108	priv->cur_adap.dev.parent = &adap->dev;
    109	priv->cur_adap.class = adap->class;
    110	priv->cur_adap.retries = adap->retries;
    111	priv->cur_adap.timeout = adap->timeout;
    112	priv->cur_adap.quirks = adap->quirks;
    113	priv->cur_adap.dev.of_node = priv->dev->of_node;
    114	ret = i2c_add_adapter(&priv->cur_adap);
    115	if (ret < 0)
    116		goto err_with_put;
    117
    118	return 0;
    119
    120 err_with_put:
    121	i2c_put_adapter(adap);
    122 err_with_revert:
    123	of_changeset_revert(&priv->chan[new_chan].chgset);
    124 err:
    125	dev_err(priv->dev, "failed to setup demux-adapter %d (%d)\n", new_chan, ret);
    126	priv->cur_chan = -EINVAL;
    127	return ret;
    128}
    129
    130static int i2c_demux_deactivate_master(struct i2c_demux_pinctrl_priv *priv)
    131{
    132	int ret, cur = priv->cur_chan;
    133
    134	if (cur < 0)
    135		return 0;
    136
    137	i2c_del_adapter(&priv->cur_adap);
    138	i2c_put_adapter(priv->chan[cur].parent_adap);
    139
    140	ret = of_changeset_revert(&priv->chan[cur].chgset);
    141
    142	priv->chan[cur].parent_adap = NULL;
    143	priv->cur_chan = -EINVAL;
    144
    145	return ret;
    146}
    147
    148static int i2c_demux_change_master(struct i2c_demux_pinctrl_priv *priv, u32 new_chan)
    149{
    150	int ret;
    151
    152	if (new_chan == priv->cur_chan)
    153		return 0;
    154
    155	ret = i2c_demux_deactivate_master(priv);
    156	if (ret)
    157		return ret;
    158
    159	return i2c_demux_activate_master(priv, new_chan);
    160}
    161
    162static ssize_t available_masters_show(struct device *dev,
    163				      struct device_attribute *attr,
    164				      char *buf)
    165{
    166	struct i2c_demux_pinctrl_priv *priv = dev_get_drvdata(dev);
    167	int count = 0, i;
    168
    169	for (i = 0; i < priv->num_chan && count < PAGE_SIZE; i++)
    170		count += scnprintf(buf + count, PAGE_SIZE - count, "%d:%pOF%c",
    171				   i, priv->chan[i].parent_np,
    172				   i == priv->num_chan - 1 ? '\n' : ' ');
    173
    174	return count;
    175}
    176static DEVICE_ATTR_RO(available_masters);
    177
    178static ssize_t current_master_show(struct device *dev,
    179				   struct device_attribute *attr,
    180				   char *buf)
    181{
    182	struct i2c_demux_pinctrl_priv *priv = dev_get_drvdata(dev);
    183
    184	return sprintf(buf, "%d\n", priv->cur_chan);
    185}
    186
    187static ssize_t current_master_store(struct device *dev,
    188				    struct device_attribute *attr,
    189				    const char *buf, size_t count)
    190{
    191	struct i2c_demux_pinctrl_priv *priv = dev_get_drvdata(dev);
    192	unsigned int val;
    193	int ret;
    194
    195	ret = kstrtouint(buf, 0, &val);
    196	if (ret < 0)
    197		return ret;
    198
    199	if (val >= priv->num_chan)
    200		return -EINVAL;
    201
    202	ret = i2c_demux_change_master(priv, val);
    203
    204	return ret < 0 ? ret : count;
    205}
    206static DEVICE_ATTR_RW(current_master);
    207
    208static int i2c_demux_pinctrl_probe(struct platform_device *pdev)
    209{
    210	struct device_node *np = pdev->dev.of_node;
    211	struct i2c_demux_pinctrl_priv *priv;
    212	struct property *props;
    213	int num_chan, i, j, err;
    214
    215	num_chan = of_count_phandle_with_args(np, "i2c-parent", NULL);
    216	if (num_chan < 2) {
    217		dev_err(&pdev->dev, "Need at least two I2C masters to switch\n");
    218		return -EINVAL;
    219	}
    220
    221	priv = devm_kzalloc(&pdev->dev, struct_size(priv, chan, num_chan),
    222			    GFP_KERNEL);
    223
    224	props = devm_kcalloc(&pdev->dev, num_chan, sizeof(*props), GFP_KERNEL);
    225
    226	if (!priv || !props)
    227		return -ENOMEM;
    228
    229	err = of_property_read_string(np, "i2c-bus-name", &priv->bus_name);
    230	if (err)
    231		return err;
    232
    233	for (i = 0; i < num_chan; i++) {
    234		struct device_node *adap_np;
    235
    236		adap_np = of_parse_phandle(np, "i2c-parent", i);
    237		if (!adap_np) {
    238			dev_err(&pdev->dev, "can't get phandle for parent %d\n", i);
    239			err = -ENOENT;
    240			goto err_rollback;
    241		}
    242		priv->chan[i].parent_np = adap_np;
    243
    244		props[i].name = devm_kstrdup(&pdev->dev, "status", GFP_KERNEL);
    245		props[i].value = devm_kstrdup(&pdev->dev, "ok", GFP_KERNEL);
    246		props[i].length = 3;
    247
    248		of_changeset_init(&priv->chan[i].chgset);
    249		of_changeset_update_property(&priv->chan[i].chgset, adap_np, &props[i]);
    250	}
    251
    252	priv->num_chan = num_chan;
    253	priv->dev = &pdev->dev;
    254
    255	platform_set_drvdata(pdev, priv);
    256
    257	pm_runtime_no_callbacks(&pdev->dev);
    258
    259	/* switch to first parent as active master */
    260	i2c_demux_activate_master(priv, 0);
    261
    262	err = device_create_file(&pdev->dev, &dev_attr_available_masters);
    263	if (err)
    264		goto err_rollback_activation;
    265
    266	err = device_create_file(&pdev->dev, &dev_attr_current_master);
    267	if (err)
    268		goto err_rollback_available;
    269
    270	return 0;
    271
    272err_rollback_available:
    273	device_remove_file(&pdev->dev, &dev_attr_available_masters);
    274err_rollback_activation:
    275	i2c_demux_deactivate_master(priv);
    276err_rollback:
    277	for (j = 0; j < i; j++) {
    278		of_node_put(priv->chan[j].parent_np);
    279		of_changeset_destroy(&priv->chan[j].chgset);
    280	}
    281
    282	return err;
    283}
    284
    285static int i2c_demux_pinctrl_remove(struct platform_device *pdev)
    286{
    287	struct i2c_demux_pinctrl_priv *priv = platform_get_drvdata(pdev);
    288	int i;
    289
    290	device_remove_file(&pdev->dev, &dev_attr_current_master);
    291	device_remove_file(&pdev->dev, &dev_attr_available_masters);
    292
    293	i2c_demux_deactivate_master(priv);
    294
    295	for (i = 0; i < priv->num_chan; i++) {
    296		of_node_put(priv->chan[i].parent_np);
    297		of_changeset_destroy(&priv->chan[i].chgset);
    298	}
    299
    300	return 0;
    301}
    302
    303static const struct of_device_id i2c_demux_pinctrl_of_match[] = {
    304	{ .compatible = "i2c-demux-pinctrl", },
    305	{},
    306};
    307MODULE_DEVICE_TABLE(of, i2c_demux_pinctrl_of_match);
    308
    309static struct platform_driver i2c_demux_pinctrl_driver = {
    310	.driver	= {
    311		.name = "i2c-demux-pinctrl",
    312		.of_match_table = i2c_demux_pinctrl_of_match,
    313	},
    314	.probe	= i2c_demux_pinctrl_probe,
    315	.remove	= i2c_demux_pinctrl_remove,
    316};
    317module_platform_driver(i2c_demux_pinctrl_driver);
    318
    319MODULE_DESCRIPTION("pinctrl-based I2C demux driver");
    320MODULE_AUTHOR("Wolfram Sang <wsa@sang-engineering.com>");
    321MODULE_LICENSE("GPL v2");
    322MODULE_ALIAS("platform:i2c-demux-pinctrl");