1 /* 2 * drivers/i2c/muxes/i2c-mux-mlxcpld.c 3 * Copyright (c) 2016 Mellanox Technologies. All rights reserved. 4 * Copyright (c) 2016 Michael Shych <michaels@mellanox.com> 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. Neither the names of the copyright holders nor the names of its 15 * contributors may be used to endorse or promote products derived from 16 * this software without specific prior written permission. 17 * 18 * Alternatively, this software may be distributed under the terms of the 19 * GNU General Public License ("GPL") version 2 as published by the Free 20 * Software Foundation. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 23 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 26 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 27 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 28 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 29 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 30 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 31 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 32 * POSSIBILITY OF SUCH DAMAGE. 33 */ 34 35 #include <linux/device.h> 36 #include <linux/i2c.h> 37 #include <linux/i2c-mux.h> 38 #include <linux/io.h> 39 #include <linux/init.h> 40 #include <linux/module.h> 41 #include <linux/platform_device.h> 42 #include <linux/slab.h> 43 #include <linux/version.h> 44 #include <linux/i2c/mlxcpld.h> 45 46 #define CPLD_MUX_MAX_NCHANS 8 47 48 /* mlxcpld_mux - mux control structure: 49 * @last_chan - last register value 50 * @client - I2C device client 51 */ 52 struct mlxcpld_mux { 53 u8 last_chan; 54 struct i2c_client *client; 55 }; 56 57 /* MUX logic description. 58 * Driver can support different mux control logic, according to CPLD 59 * implementation. 60 * 61 * Connectivity schema. 62 * 63 * i2c-mlxcpld Digital Analog 64 * driver 65 * *--------* * -> mux1 (virt bus2) -> mux -> | 66 * | I2CLPC | i2c physical * -> mux2 (virt bus3) -> mux -> | 67 * | bridge | bus 1 *---------* | 68 * | logic |---------------------> * mux reg * | 69 * | in CPLD| *---------* | 70 * *--------* i2c-mux-mlxpcld ^ * -> muxn (virt busn) -> mux -> | 71 * | driver | | 72 * | *---------------* | Devices 73 * | * CPLD (i2c bus)* select | 74 * | * registers for *--------* 75 * | * mux selection * deselect 76 * | *---------------* 77 * | | 78 * <--------> <-----------> 79 * i2c cntrl Board cntrl reg 80 * reg space space (mux select, 81 * IO, LED, WD, info) 82 * 83 */ 84 85 static const struct i2c_device_id mlxcpld_mux_id[] = { 86 { "mlxcpld_mux_module", 0 }, 87 { } 88 }; 89 MODULE_DEVICE_TABLE(i2c, mlxcpld_mux_id); 90 91 /* Write to mux register. Don't use i2c_transfer() and i2c_smbus_xfer() 92 * for this as they will try to lock adapter a second time. 93 */ 94 static int mlxcpld_mux_reg_write(struct i2c_adapter *adap, 95 struct i2c_client *client, u8 val) 96 { 97 struct mlxcpld_mux_plat_data *pdata = dev_get_platdata(&client->dev); 98 99 if (adap->algo->master_xfer) { 100 struct i2c_msg msg; 101 u8 msgbuf[] = {pdata->sel_reg_addr, val}; 102 103 msg.addr = client->addr; 104 msg.flags = 0; 105 msg.len = 2; 106 msg.buf = msgbuf; 107 return __i2c_transfer(adap, &msg, 1); 108 } else if (adap->algo->smbus_xfer) { 109 union i2c_smbus_data data; 110 111 data.byte = val; 112 return adap->algo->smbus_xfer(adap, client->addr, 113 client->flags, I2C_SMBUS_WRITE, 114 pdata->sel_reg_addr, 115 I2C_SMBUS_BYTE_DATA, &data); 116 } else 117 return -ENODEV; 118 } 119 120 static int mlxcpld_mux_select_chan(struct i2c_mux_core *muxc, u32 chan) 121 { 122 struct mlxcpld_mux *data = i2c_mux_priv(muxc); 123 struct i2c_client *client = data->client; 124 u8 regval = chan + 1; 125 int err = 0; 126 127 /* Only select the channel if its different from the last channel */ 128 if (data->last_chan != regval) { 129 err = mlxcpld_mux_reg_write(muxc->parent, client, regval); 130 if (err) 131 data->last_chan = 0; 132 else 133 data->last_chan = regval; 134 } 135 136 return err; 137 } 138 139 static int mlxcpld_mux_deselect(struct i2c_mux_core *muxc, u32 chan) 140 { 141 struct mlxcpld_mux *data = i2c_mux_priv(muxc); 142 struct i2c_client *client = data->client; 143 144 /* Deselect active channel */ 145 data->last_chan = 0; 146 147 return mlxcpld_mux_reg_write(muxc->parent, client, data->last_chan); 148 } 149 150 /* Probe/reomove functions */ 151 static int mlxcpld_mux_probe(struct i2c_client *client, 152 const struct i2c_device_id *id) 153 { 154 struct i2c_adapter *adap = to_i2c_adapter(client->dev.parent); 155 struct mlxcpld_mux_plat_data *pdata = dev_get_platdata(&client->dev); 156 struct i2c_mux_core *muxc; 157 int num, force; 158 struct mlxcpld_mux *data; 159 int err; 160 161 if (!pdata) 162 return -EINVAL; 163 164 if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) 165 return -ENODEV; 166 167 muxc = i2c_mux_alloc(adap, &client->dev, CPLD_MUX_MAX_NCHANS, 168 sizeof(*data), 0, mlxcpld_mux_select_chan, 169 mlxcpld_mux_deselect); 170 if (!muxc) 171 return -ENOMEM; 172 173 data = i2c_mux_priv(muxc); 174 i2c_set_clientdata(client, muxc); 175 data->client = client; 176 data->last_chan = 0; /* force the first selection */ 177 178 /* Create an adapter for each channel. */ 179 for (num = 0; num < CPLD_MUX_MAX_NCHANS; num++) { 180 if (num >= pdata->num_adaps) 181 /* discard unconfigured channels */ 182 break; 183 184 force = pdata->adap_ids[num]; 185 186 err = i2c_mux_add_adapter(muxc, force, num, 0); 187 if (err) 188 goto virt_reg_failed; 189 } 190 191 return 0; 192 193 virt_reg_failed: 194 i2c_mux_del_adapters(muxc); 195 return err; 196 } 197 198 static int mlxcpld_mux_remove(struct i2c_client *client) 199 { 200 struct i2c_mux_core *muxc = i2c_get_clientdata(client); 201 202 i2c_mux_del_adapters(muxc); 203 return 0; 204 } 205 206 static struct i2c_driver mlxcpld_mux_driver = { 207 .driver = { 208 .name = "mlxcpld-mux", 209 }, 210 .probe = mlxcpld_mux_probe, 211 .remove = mlxcpld_mux_remove, 212 .id_table = mlxcpld_mux_id, 213 }; 214 215 module_i2c_driver(mlxcpld_mux_driver); 216 217 MODULE_AUTHOR("Michael Shych (michaels@mellanox.com)"); 218 MODULE_DESCRIPTION("Mellanox I2C-CPLD-MUX driver"); 219 MODULE_LICENSE("Dual BSD/GPL"); 220 MODULE_ALIAS("platform:i2c-mux-mlxcpld"); 221