i2c-mux-mlxcpld.c 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  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. union i2c_smbus_data data = { .byte = val };
  92. return __i2c_smbus_xfer(adap, client->addr, client->flags,
  93. I2C_SMBUS_WRITE, pdata->sel_reg_addr,
  94. I2C_SMBUS_BYTE_DATA, &data);
  95. }
  96. static int mlxcpld_mux_select_chan(struct i2c_mux_core *muxc, u32 chan)
  97. {
  98. struct mlxcpld_mux *data = i2c_mux_priv(muxc);
  99. struct i2c_client *client = data->client;
  100. u8 regval = chan + 1;
  101. int err = 0;
  102. /* Only select the channel if its different from the last channel */
  103. if (data->last_chan != regval) {
  104. err = mlxcpld_mux_reg_write(muxc->parent, client, regval);
  105. data->last_chan = err < 0 ? 0 : regval;
  106. }
  107. return err;
  108. }
  109. static int mlxcpld_mux_deselect(struct i2c_mux_core *muxc, u32 chan)
  110. {
  111. struct mlxcpld_mux *data = i2c_mux_priv(muxc);
  112. struct i2c_client *client = data->client;
  113. /* Deselect active channel */
  114. data->last_chan = 0;
  115. return mlxcpld_mux_reg_write(muxc->parent, client, data->last_chan);
  116. }
  117. /* Probe/reomove functions */
  118. static int mlxcpld_mux_probe(struct i2c_client *client,
  119. const struct i2c_device_id *id)
  120. {
  121. struct i2c_adapter *adap = to_i2c_adapter(client->dev.parent);
  122. struct mlxcpld_mux_plat_data *pdata = dev_get_platdata(&client->dev);
  123. struct i2c_mux_core *muxc;
  124. int num, force;
  125. struct mlxcpld_mux *data;
  126. int err;
  127. if (!pdata)
  128. return -EINVAL;
  129. if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
  130. return -ENODEV;
  131. muxc = i2c_mux_alloc(adap, &client->dev, CPLD_MUX_MAX_NCHANS,
  132. sizeof(*data), 0, mlxcpld_mux_select_chan,
  133. mlxcpld_mux_deselect);
  134. if (!muxc)
  135. return -ENOMEM;
  136. data = i2c_mux_priv(muxc);
  137. i2c_set_clientdata(client, muxc);
  138. data->client = client;
  139. data->last_chan = 0; /* force the first selection */
  140. /* Create an adapter for each channel. */
  141. for (num = 0; num < CPLD_MUX_MAX_NCHANS; num++) {
  142. if (num >= pdata->num_adaps)
  143. /* discard unconfigured channels */
  144. break;
  145. force = pdata->adap_ids[num];
  146. err = i2c_mux_add_adapter(muxc, force, num, 0);
  147. if (err)
  148. goto virt_reg_failed;
  149. }
  150. return 0;
  151. virt_reg_failed:
  152. i2c_mux_del_adapters(muxc);
  153. return err;
  154. }
  155. static int mlxcpld_mux_remove(struct i2c_client *client)
  156. {
  157. struct i2c_mux_core *muxc = i2c_get_clientdata(client);
  158. i2c_mux_del_adapters(muxc);
  159. return 0;
  160. }
  161. static struct i2c_driver mlxcpld_mux_driver = {
  162. .driver = {
  163. .name = "mlxcpld-mux",
  164. },
  165. .probe = mlxcpld_mux_probe,
  166. .remove = mlxcpld_mux_remove,
  167. .id_table = mlxcpld_mux_id,
  168. };
  169. module_i2c_driver(mlxcpld_mux_driver);
  170. MODULE_AUTHOR("Michael Shych (michaels@mellanox.com)");
  171. MODULE_DESCRIPTION("Mellanox I2C-CPLD-MUX driver");
  172. MODULE_LICENSE("Dual BSD/GPL");
  173. MODULE_ALIAS("platform:i2c-mux-mlxcpld");