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