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_data/x86/mlxcpld.h> 42 #include <linux/platform_device.h> 43 #include <linux/slab.h> 44 45 #define CPLD_MUX_MAX_NCHANS 8 46 47 /* mlxcpld_mux - mux control structure: 48 * @last_chan - last register value 49 * @client - I2C device client 50 */ 51 struct mlxcpld_mux { 52 u8 last_chan; 53 struct i2c_client *client; 54 }; 55 56 /* MUX logic description. 57 * Driver can support different mux control logic, according to CPLD 58 * implementation. 59 * 60 * Connectivity schema. 61 * 62 * i2c-mlxcpld Digital Analog 63 * driver 64 * *--------* * -> mux1 (virt bus2) -> mux -> | 65 * | I2CLPC | i2c physical * -> mux2 (virt bus3) -> mux -> | 66 * | bridge | bus 1 *---------* | 67 * | logic |---------------------> * mux reg * | 68 * | in CPLD| *---------* | 69 * *--------* i2c-mux-mlxpcld ^ * -> muxn (virt busn) -> mux -> | 70 * | driver | | 71 * | *---------------* | Devices 72 * | * CPLD (i2c bus)* select | 73 * | * registers for *--------* 74 * | * mux selection * deselect 75 * | *---------------* 76 * | | 77 * <--------> <-----------> 78 * i2c cntrl Board cntrl reg 79 * reg space space (mux select, 80 * IO, LED, WD, info) 81 * 82 */ 83 84 static const struct i2c_device_id mlxcpld_mux_id[] = { 85 { "mlxcpld_mux_module", 0 }, 86 { } 87 }; 88 MODULE_DEVICE_TABLE(i2c, mlxcpld_mux_id); 89 90 /* Write to mux register. Don't use i2c_transfer() and i2c_smbus_xfer() 91 * for this as they will try to lock adapter a second time. 92 */ 93 static int mlxcpld_mux_reg_write(struct i2c_adapter *adap, 94 struct i2c_client *client, u8 val) 95 { 96 struct mlxcpld_mux_plat_data *pdata = dev_get_platdata(&client->dev); 97 union i2c_smbus_data data = { .byte = val }; 98 99 return __i2c_smbus_xfer(adap, client->addr, client->flags, 100 I2C_SMBUS_WRITE, pdata->sel_reg_addr, 101 I2C_SMBUS_BYTE_DATA, &data); 102 } 103 104 static int mlxcpld_mux_select_chan(struct i2c_mux_core *muxc, u32 chan) 105 { 106 struct mlxcpld_mux *data = i2c_mux_priv(muxc); 107 struct i2c_client *client = data->client; 108 u8 regval = chan + 1; 109 int err = 0; 110 111 /* Only select the channel if its different from the last channel */ 112 if (data->last_chan != regval) { 113 err = mlxcpld_mux_reg_write(muxc->parent, client, regval); 114 data->last_chan = err < 0 ? 0 : regval; 115 } 116 117 return err; 118 } 119 120 static int mlxcpld_mux_deselect(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 125 /* Deselect active channel */ 126 data->last_chan = 0; 127 128 return mlxcpld_mux_reg_write(muxc->parent, client, data->last_chan); 129 } 130 131 /* Probe/reomove functions */ 132 static int mlxcpld_mux_probe(struct i2c_client *client, 133 const struct i2c_device_id *id) 134 { 135 struct i2c_adapter *adap = to_i2c_adapter(client->dev.parent); 136 struct mlxcpld_mux_plat_data *pdata = dev_get_platdata(&client->dev); 137 struct i2c_mux_core *muxc; 138 int num, force; 139 struct mlxcpld_mux *data; 140 int err; 141 142 if (!pdata) 143 return -EINVAL; 144 145 if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) 146 return -ENODEV; 147 148 muxc = i2c_mux_alloc(adap, &client->dev, CPLD_MUX_MAX_NCHANS, 149 sizeof(*data), 0, mlxcpld_mux_select_chan, 150 mlxcpld_mux_deselect); 151 if (!muxc) 152 return -ENOMEM; 153 154 data = i2c_mux_priv(muxc); 155 i2c_set_clientdata(client, muxc); 156 data->client = client; 157 data->last_chan = 0; /* force the first selection */ 158 159 /* Create an adapter for each channel. */ 160 for (num = 0; num < CPLD_MUX_MAX_NCHANS; num++) { 161 if (num >= pdata->num_adaps) 162 /* discard unconfigured channels */ 163 break; 164 165 force = pdata->adap_ids[num]; 166 167 err = i2c_mux_add_adapter(muxc, force, num, 0); 168 if (err) 169 goto virt_reg_failed; 170 } 171 172 return 0; 173 174 virt_reg_failed: 175 i2c_mux_del_adapters(muxc); 176 return err; 177 } 178 179 static int mlxcpld_mux_remove(struct i2c_client *client) 180 { 181 struct i2c_mux_core *muxc = i2c_get_clientdata(client); 182 183 i2c_mux_del_adapters(muxc); 184 return 0; 185 } 186 187 static struct i2c_driver mlxcpld_mux_driver = { 188 .driver = { 189 .name = "mlxcpld-mux", 190 }, 191 .probe = mlxcpld_mux_probe, 192 .remove = mlxcpld_mux_remove, 193 .id_table = mlxcpld_mux_id, 194 }; 195 196 module_i2c_driver(mlxcpld_mux_driver); 197 198 MODULE_AUTHOR("Michael Shych (michaels@mellanox.com)"); 199 MODULE_DESCRIPTION("Mellanox I2C-CPLD-MUX driver"); 200 MODULE_LICENSE("Dual BSD/GPL"); 201 MODULE_ALIAS("platform:i2c-mux-mlxcpld"); 202