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-mlxcpld.c (5263B)


      1// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
      2/*
      3 * Mellanox i2c mux driver
      4 *
      5 * Copyright (C) 2016-2020 Mellanox Technologies
      6 */
      7
      8#include <linux/device.h>
      9#include <linux/i2c.h>
     10#include <linux/i2c-mux.h>
     11#include <linux/io.h>
     12#include <linux/init.h>
     13#include <linux/module.h>
     14#include <linux/platform_data/mlxcpld.h>
     15#include <linux/platform_device.h>
     16#include <linux/slab.h>
     17
     18/* mlxcpld_mux - mux control structure:
     19 * @last_val - last selected register value or -1 if mux deselected
     20 * @client - I2C device client
     21 * @pdata: platform data
     22 */
     23struct mlxcpld_mux {
     24	int last_val;
     25	struct i2c_client *client;
     26	struct mlxcpld_mux_plat_data pdata;
     27};
     28
     29/* MUX logic description.
     30 * Driver can support different mux control logic, according to CPLD
     31 * implementation.
     32 *
     33 * Connectivity schema.
     34 *
     35 * i2c-mlxcpld                                 Digital               Analog
     36 * driver
     37 * *--------*                                 * -> mux1 (virt bus2) -> mux -> |
     38 * | I2CLPC | i2c physical                    * -> mux2 (virt bus3) -> mux -> |
     39 * | bridge | bus 1                 *---------*                               |
     40 * | logic  |---------------------> * mux reg *                               |
     41 * | in CPLD|                       *---------*                               |
     42 * *--------*   i2c-mux-mlxpcld          ^    * -> muxn (virt busn) -> mux -> |
     43 *     |        driver                   |                                    |
     44 *     |        *---------------*        |                              Devices
     45 *     |        * CPLD (i2c bus)* select |
     46 *     |        * registers for *--------*
     47 *     |        * mux selection * deselect
     48 *     |        *---------------*
     49 *     |                 |
     50 * <-------->     <----------->
     51 * i2c cntrl      Board cntrl reg
     52 * reg space      space (mux select,
     53 *                IO, LED, WD, info)
     54 *
     55 */
     56
     57/* Write to mux register. Don't use i2c_transfer() and i2c_smbus_xfer()
     58 * for this as they will try to lock adapter a second time.
     59 */
     60static int mlxcpld_mux_reg_write(struct i2c_adapter *adap,
     61				 struct mlxcpld_mux *mux, u32 val)
     62{
     63	struct i2c_client *client = mux->client;
     64	union i2c_smbus_data data;
     65	struct i2c_msg msg;
     66	u8 buf[3];
     67
     68	switch (mux->pdata.reg_size) {
     69	case 1:
     70		data.byte = val;
     71		return __i2c_smbus_xfer(adap, client->addr, client->flags,
     72					I2C_SMBUS_WRITE, mux->pdata.sel_reg_addr,
     73					I2C_SMBUS_BYTE_DATA, &data);
     74	case 2:
     75		buf[0] = mux->pdata.sel_reg_addr >> 8;
     76		buf[1] = mux->pdata.sel_reg_addr;
     77		buf[2] = val;
     78		msg.addr = client->addr;
     79		msg.buf = buf;
     80		msg.len = mux->pdata.reg_size + 1;
     81		msg.flags = 0;
     82		return __i2c_transfer(adap, &msg, 1);
     83	default:
     84		return -EINVAL;
     85	}
     86}
     87
     88static int mlxcpld_mux_select_chan(struct i2c_mux_core *muxc, u32 chan)
     89{
     90	struct mlxcpld_mux *mux = i2c_mux_priv(muxc);
     91	u32 regval = chan;
     92	int err = 0;
     93
     94	if (mux->pdata.reg_size == 1)
     95		regval += 1;
     96
     97	/* Only select the channel if its different from the last channel */
     98	if (mux->last_val != regval) {
     99		err = mlxcpld_mux_reg_write(muxc->parent, mux, regval);
    100		mux->last_val = err < 0 ? -1 : regval;
    101	}
    102
    103	return err;
    104}
    105
    106static int mlxcpld_mux_deselect(struct i2c_mux_core *muxc, u32 chan)
    107{
    108	struct mlxcpld_mux *mux = i2c_mux_priv(muxc);
    109
    110	/* Deselect active channel */
    111	mux->last_val = -1;
    112
    113	return mlxcpld_mux_reg_write(muxc->parent, mux, 0);
    114}
    115
    116/* Probe/reomove functions */
    117static int mlxcpld_mux_probe(struct platform_device *pdev)
    118{
    119	struct mlxcpld_mux_plat_data *pdata = dev_get_platdata(&pdev->dev);
    120	struct i2c_client *client = to_i2c_client(pdev->dev.parent);
    121	struct i2c_mux_core *muxc;
    122	struct mlxcpld_mux *data;
    123	int num, err;
    124	u32 func;
    125
    126	if (!pdata)
    127		return -EINVAL;
    128
    129	switch (pdata->reg_size) {
    130	case 1:
    131		func = I2C_FUNC_SMBUS_WRITE_BYTE_DATA;
    132		break;
    133	case 2:
    134		func = I2C_FUNC_I2C;
    135		break;
    136	default:
    137		return -EINVAL;
    138	}
    139
    140	if (!i2c_check_functionality(client->adapter, func))
    141		return -ENODEV;
    142
    143	muxc = i2c_mux_alloc(client->adapter, &pdev->dev, pdata->num_adaps,
    144			     sizeof(*data), 0, mlxcpld_mux_select_chan,
    145			     mlxcpld_mux_deselect);
    146	if (!muxc)
    147		return -ENOMEM;
    148
    149	platform_set_drvdata(pdev, muxc);
    150	data = i2c_mux_priv(muxc);
    151	data->client = client;
    152	memcpy(&data->pdata, pdata, sizeof(*pdata));
    153	data->last_val = -1; /* force the first selection */
    154
    155	/* Create an adapter for each channel. */
    156	for (num = 0; num < pdata->num_adaps; num++) {
    157		err = i2c_mux_add_adapter(muxc, 0, pdata->chan_ids[num], 0);
    158		if (err)
    159			goto virt_reg_failed;
    160	}
    161
    162	/* Notify caller when all channels' adapters are created. */
    163	if (pdata->completion_notify)
    164		pdata->completion_notify(pdata->handle, muxc->parent, muxc->adapter);
    165
    166	return 0;
    167
    168virt_reg_failed:
    169	i2c_mux_del_adapters(muxc);
    170	return err;
    171}
    172
    173static int mlxcpld_mux_remove(struct platform_device *pdev)
    174{
    175	struct i2c_mux_core *muxc = platform_get_drvdata(pdev);
    176
    177	i2c_mux_del_adapters(muxc);
    178	return 0;
    179}
    180
    181static struct platform_driver mlxcpld_mux_driver = {
    182	.driver = {
    183		.name = "i2c-mux-mlxcpld",
    184	},
    185	.probe = mlxcpld_mux_probe,
    186	.remove = mlxcpld_mux_remove,
    187};
    188
    189module_platform_driver(mlxcpld_mux_driver);
    190
    191MODULE_AUTHOR("Michael Shych (michaels@mellanox.com)");
    192MODULE_DESCRIPTION("Mellanox I2C-CPLD-MUX driver");
    193MODULE_LICENSE("Dual BSD/GPL");
    194MODULE_ALIAS("platform:i2c-mux-mlxcpld");