0001
0002
0003
0004
0005
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
0019
0020
0021
0022
0023 struct mlxcpld_mux {
0024 int last_val;
0025 struct i2c_client *client;
0026 struct mlxcpld_mux_plat_data pdata;
0027 };
0028
0029
0030
0031
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043
0044
0045
0046
0047
0048
0049
0050
0051
0052
0053
0054
0055
0056
0057
0058
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
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
0111 mux->last_val = -1;
0112
0113 return mlxcpld_mux_reg_write(muxc->parent, mux, 0);
0114 }
0115
0116
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;
0154
0155
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
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");