Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
0002 /*
0003  * Mellanox i2c mux driver
0004  *
0005  * Copyright (C) 2016-2020 Mellanox Technologies
0006  */
0007 
0008 #include <linux/device.h>
0009 #include <linux/i2c.h>
0010 #include <linux/i2c-mux.h>
0011 #include <linux/io.h>
0012 #include <linux/init.h>
0013 #include <linux/module.h>
0014 #include <linux/platform_data/mlxcpld.h>
0015 #include <linux/platform_device.h>
0016 #include <linux/slab.h>
0017 
0018 /* mlxcpld_mux - mux control structure:
0019  * @last_val - last selected register value or -1 if mux deselected
0020  * @client - I2C device client
0021  * @pdata: platform data
0022  */
0023 struct mlxcpld_mux {
0024     int last_val;
0025     struct i2c_client *client;
0026     struct mlxcpld_mux_plat_data pdata;
0027 };
0028 
0029 /* MUX logic description.
0030  * Driver can support different mux control logic, according to CPLD
0031  * implementation.
0032  *
0033  * Connectivity schema.
0034  *
0035  * i2c-mlxcpld                                 Digital               Analog
0036  * driver
0037  * *--------*                                 * -> mux1 (virt bus2) -> mux -> |
0038  * | I2CLPC | i2c physical                    * -> mux2 (virt bus3) -> mux -> |
0039  * | bridge | bus 1                 *---------*                               |
0040  * | logic  |---------------------> * mux reg *                               |
0041  * | in CPLD|                       *---------*                               |
0042  * *--------*   i2c-mux-mlxpcld          ^    * -> muxn (virt busn) -> mux -> |
0043  *     |        driver                   |                                    |
0044  *     |        *---------------*        |                              Devices
0045  *     |        * CPLD (i2c bus)* select |
0046  *     |        * registers for *--------*
0047  *     |        * mux selection * deselect
0048  *     |        *---------------*
0049  *     |                 |
0050  * <-------->     <----------->
0051  * i2c cntrl      Board cntrl reg
0052  * reg space      space (mux select,
0053  *                IO, LED, WD, info)
0054  *
0055  */
0056 
0057 /* Write to mux register. Don't use i2c_transfer() and i2c_smbus_xfer()
0058  * for this as they will try to lock adapter a second time.
0059  */
0060 static int mlxcpld_mux_reg_write(struct i2c_adapter *adap,
0061                  struct mlxcpld_mux *mux, u32 val)
0062 {
0063     struct i2c_client *client = mux->client;
0064     union i2c_smbus_data data;
0065     struct i2c_msg msg;
0066     u8 buf[3];
0067 
0068     switch (mux->pdata.reg_size) {
0069     case 1:
0070         data.byte = val;
0071         return __i2c_smbus_xfer(adap, client->addr, client->flags,
0072                     I2C_SMBUS_WRITE, mux->pdata.sel_reg_addr,
0073                     I2C_SMBUS_BYTE_DATA, &data);
0074     case 2:
0075         buf[0] = mux->pdata.sel_reg_addr >> 8;
0076         buf[1] = mux->pdata.sel_reg_addr;
0077         buf[2] = val;
0078         msg.addr = client->addr;
0079         msg.buf = buf;
0080         msg.len = mux->pdata.reg_size + 1;
0081         msg.flags = 0;
0082         return __i2c_transfer(adap, &msg, 1);
0083     default:
0084         return -EINVAL;
0085     }
0086 }
0087 
0088 static int mlxcpld_mux_select_chan(struct i2c_mux_core *muxc, u32 chan)
0089 {
0090     struct mlxcpld_mux *mux = i2c_mux_priv(muxc);
0091     u32 regval = chan;
0092     int err = 0;
0093 
0094     if (mux->pdata.reg_size == 1)
0095         regval += 1;
0096 
0097     /* Only select the channel if its different from the last channel */
0098     if (mux->last_val != regval) {
0099         err = mlxcpld_mux_reg_write(muxc->parent, mux, regval);
0100         mux->last_val = err < 0 ? -1 : regval;
0101     }
0102 
0103     return err;
0104 }
0105 
0106 static int mlxcpld_mux_deselect(struct i2c_mux_core *muxc, u32 chan)
0107 {
0108     struct mlxcpld_mux *mux = i2c_mux_priv(muxc);
0109 
0110     /* Deselect active channel */
0111     mux->last_val = -1;
0112 
0113     return mlxcpld_mux_reg_write(muxc->parent, mux, 0);
0114 }
0115 
0116 /* Probe/reomove functions */
0117 static int mlxcpld_mux_probe(struct platform_device *pdev)
0118 {
0119     struct mlxcpld_mux_plat_data *pdata = dev_get_platdata(&pdev->dev);
0120     struct i2c_client *client = to_i2c_client(pdev->dev.parent);
0121     struct i2c_mux_core *muxc;
0122     struct mlxcpld_mux *data;
0123     int num, err;
0124     u32 func;
0125 
0126     if (!pdata)
0127         return -EINVAL;
0128 
0129     switch (pdata->reg_size) {
0130     case 1:
0131         func = I2C_FUNC_SMBUS_WRITE_BYTE_DATA;
0132         break;
0133     case 2:
0134         func = I2C_FUNC_I2C;
0135         break;
0136     default:
0137         return -EINVAL;
0138     }
0139 
0140     if (!i2c_check_functionality(client->adapter, func))
0141         return -ENODEV;
0142 
0143     muxc = i2c_mux_alloc(client->adapter, &pdev->dev, pdata->num_adaps,
0144                  sizeof(*data), 0, mlxcpld_mux_select_chan,
0145                  mlxcpld_mux_deselect);
0146     if (!muxc)
0147         return -ENOMEM;
0148 
0149     platform_set_drvdata(pdev, muxc);
0150     data = i2c_mux_priv(muxc);
0151     data->client = client;
0152     memcpy(&data->pdata, pdata, sizeof(*pdata));
0153     data->last_val = -1; /* force the first selection */
0154 
0155     /* Create an adapter for each channel. */
0156     for (num = 0; num < pdata->num_adaps; num++) {
0157         err = i2c_mux_add_adapter(muxc, 0, pdata->chan_ids[num], 0);
0158         if (err)
0159             goto virt_reg_failed;
0160     }
0161 
0162     /* Notify caller when all channels' adapters are created. */
0163     if (pdata->completion_notify)
0164         pdata->completion_notify(pdata->handle, muxc->parent, muxc->adapter);
0165 
0166     return 0;
0167 
0168 virt_reg_failed:
0169     i2c_mux_del_adapters(muxc);
0170     return err;
0171 }
0172 
0173 static int mlxcpld_mux_remove(struct platform_device *pdev)
0174 {
0175     struct i2c_mux_core *muxc = platform_get_drvdata(pdev);
0176 
0177     i2c_mux_del_adapters(muxc);
0178     return 0;
0179 }
0180 
0181 static struct platform_driver mlxcpld_mux_driver = {
0182     .driver = {
0183         .name = "i2c-mux-mlxcpld",
0184     },
0185     .probe = mlxcpld_mux_probe,
0186     .remove = mlxcpld_mux_remove,
0187 };
0188 
0189 module_platform_driver(mlxcpld_mux_driver);
0190 
0191 MODULE_AUTHOR("Michael Shych (michaels@mellanox.com)");
0192 MODULE_DESCRIPTION("Mellanox I2C-CPLD-MUX driver");
0193 MODULE_LICENSE("Dual BSD/GPL");
0194 MODULE_ALIAS("platform:i2c-mux-mlxcpld");