i2c-mux-mlxcpld.c 6.8 KB

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