pca9540bdp.c 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  1. /****************************************************************************
  2. * drivers/i2c/pca9540bdp.c
  3. * Driver for the PCA9540BDP i2c multiplexer
  4. *
  5. * Copyright (C) 2018 Giorgio Groß. All rights reserved.
  6. * Author: Giorgio Groß <giorgio.gross@robodev.eu>
  7. *
  8. * Redistribution and use in source and binary forms, with or without
  9. * modification, are permitted provided that the following conditions
  10. * are met:
  11. *
  12. * 1. Redistributions of source code must retain the above copyright
  13. * notice, this list of conditions and the following disclaimer.
  14. * 2. Redistributions in binary form must reproduce the above copyright
  15. * notice, this list of conditions and the following disclaimer in
  16. * the documentation and/or other materials provided with the
  17. * distribution.
  18. * 3. Neither the name NuttX nor the names of its contributors may be
  19. * used to endorse or promote products derived from this software
  20. * without specific prior written permission.
  21. *
  22. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  23. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  24. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  25. * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  26. * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  27. * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  28. * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
  29. * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
  30. * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  31. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
  32. * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  33. * POSSIBILITY OF SUCH DAMAGE.
  34. *
  35. ****************************************************************************/
  36. /****************************************************************************
  37. * Included Files
  38. ****************************************************************************/
  39. #include <nuttx/config.h>
  40. #include <stdlib.h>
  41. #include <fixedmath.h>
  42. #include <assert.h>
  43. #include <errno.h>
  44. #include <debug.h>
  45. #include <nuttx/kmalloc.h>
  46. #include <nuttx/fs/fs.h>
  47. #include <nuttx/i2c/i2c_master.h>
  48. #include "pca9540bdp.h"
  49. #include <nuttx/i2c/pca9540bdp.h>
  50. #ifdef CONFIG_I2CMULTIPLEXER_PCA9540BDP
  51. /****************************************************************************
  52. * Private Function Prototypes
  53. ****************************************************************************/
  54. /* I2C Helpers */
  55. static int pca9540bdp_write_config(FAR struct pca9540bdp_dev_s *priv,
  56. FAR uint8_t regvalue);
  57. static int pca9540bdp_read_config(FAR struct pca9540bdp_dev_s *priv,
  58. FAR uint8_t *regvalue);
  59. /* Other helpers */
  60. static int pca9540bdp_select_port(FAR struct pca9540bdp_dev_s *priv,
  61. uint8_t val);
  62. /* I2C multiplexer vtable */
  63. static int pca9540bdp_transfer_on_port (FAR struct i2c_master_s *dev,
  64. FAR struct i2c_msg_s *msgs, int count);
  65. #ifdef CONFIG_I2C_RESET
  66. static int pca9540bdp_reset_on_port (FAR struct i2c_master_s *dev);
  67. #endif
  68. /****************************************************************************
  69. * Private Data
  70. ****************************************************************************/
  71. static const struct i2c_ops_s g_i2cmux_ops =
  72. {
  73. pca9540bdp_transfer_on_port
  74. #ifdef CONFIG_I2C_RESET
  75. , pca9540bdp_reset_on_port
  76. #endif
  77. };
  78. /****************************************************************************
  79. * Private Functions
  80. ****************************************************************************/
  81. static int pca9540bdp_write_config(FAR struct pca9540bdp_dev_s *priv,
  82. FAR uint8_t regvalue)
  83. {
  84. struct i2c_config_s iconf;
  85. int ret;
  86. iconf.frequency = CONFIG_PCA9549BDP_I2C_FREQUENCY;
  87. iconf.address = priv->addr;
  88. iconf.addrlen = 7;
  89. ret = i2c_write(priv->i2c, &iconf, &regvalue, 1);
  90. i2cinfo("Write to address 0x%02X; register value: 0x%02x ret: %d\n",
  91. priv->addr, regvalue, ret);
  92. return ret;
  93. }
  94. static int pca9540bdp_read_config(FAR struct pca9540bdp_dev_s *priv,
  95. FAR uint8_t *regvalue)
  96. {
  97. struct i2c_config_s iconf;
  98. int ret;
  99. iconf.frequency = CONFIG_PCA9549BDP_I2C_FREQUENCY;
  100. iconf.address = priv->addr;
  101. iconf.addrlen = 7;
  102. ret = i2c_read(priv->i2c, &iconf, regvalue, 1);
  103. i2cinfo("Read from address: 0x%02X; register value: 0x%02x ret: %d\n",
  104. priv->addr, *regvalue, ret);
  105. return ret;
  106. }
  107. static int pca9540bdp_select_port(FAR struct pca9540bdp_dev_s *priv,
  108. uint8_t val)
  109. {
  110. if (val != PCA9540BDP_SEL_PORT0 && val != PCA9540BDP_SEL_PORT1)
  111. {
  112. /* port not supported */
  113. return -EINVAL;
  114. }
  115. if ((PCA9540BDP_ENABLE | val) == priv->state)
  116. {
  117. /* port already selected */
  118. return OK;
  119. }
  120. /* Modify state and write it to the mux. Selecting a port always enables
  121. * the device
  122. */
  123. priv->state = PCA9540BDP_ENABLE | val;
  124. return (pca9540bdp_write_config(priv, priv->state) == 0) ? OK : -ECOMM;
  125. }
  126. static int pca9540bdp_transfer_on_port(FAR struct i2c_master_s *dev,
  127. FAR struct i2c_msg_s *msgs, int count)
  128. {
  129. FAR struct i2c_port_dev_s *port_dev = (FAR struct i2c_port_dev_s *)dev;
  130. FAR struct pca9540bdp_dev_s *priv = port_dev->dev;
  131. int ret;
  132. /* select the mux port */
  133. if (pca9540bdp_select_port(priv, port_dev->port) != OK)
  134. {
  135. i2cerr("Could not select proper mux port\n");
  136. return -ECOMM; /* Signal error condition */
  137. }
  138. /* Resume the i2c transfer to the device connected to the mux port */
  139. ret = I2C_TRANSFER(priv->i2c, msgs, count);
  140. i2cinfo("Selected port %d and resumed transfer. Result: %d\n",
  141. port_dev->port, ret);
  142. return ret;
  143. }
  144. #ifdef CONFIG_I2C_RESET
  145. static int pca9540bdp_reset_on_port (FAR struct i2c_master_s *dev)
  146. {
  147. FAR struct i2c_port_dev_s *port_dev = (struct i2c_port_dev_s *)dev;
  148. FAR struct pca9540bdp_dev_s *priv = port_dev->dev;
  149. int port = port_dev->port;
  150. int ret;
  151. /* select the mux port */
  152. if (pca9540bdp_select_port(priv, port) != OK)
  153. {
  154. i2cerr("Could not select proper mux port\n");
  155. return -ECOMM; /* signal error condition */
  156. }
  157. /* resume the i2c reset for the device connected to the mux port */
  158. ret = I2C_RESET(priv->i2c);
  159. return ret;
  160. }
  161. #endif
  162. /****************************************************************************
  163. * Public Functions
  164. ****************************************************************************/
  165. /****************************************************************************
  166. * Name: pca9540bdp_lower_half
  167. *
  168. * Description:
  169. * Initialize the lower half of the PCA9540BDP by creating a i2c_master_s
  170. * for the virtual i2c master and link it to the associated PCA9540BDP and
  171. * its port.
  172. *
  173. * Input Parameters:
  174. * dev - Pointer to the associated PCA9540BDP
  175. * port - The port number as defined in pca9540bdp.h
  176. *
  177. * Returned Value:
  178. * Common i2c multiplexer device instance; NULL on failure.
  179. *
  180. ****************************************************************************/
  181. FAR struct i2c_master_s *
  182. pca9540bdp_lower_half(FAR struct pca9540bdp_dev_s *dev, uint8_t port)
  183. {
  184. FAR struct i2c_port_dev_s *port_dev;
  185. /* Sanity check */
  186. DEBUGASSERT(dev != NULL);
  187. /* Initialize the i2c_port_dev_s device structure */
  188. port_dev = (FAR struct i2c_port_dev_s *)
  189. kmm_malloc(sizeof(struct i2c_port_dev_s));
  190. if (port_dev == NULL)
  191. {
  192. i2cerr("ERROR: Failed to allocate i2c port lower half instance\n");
  193. return NULL;
  194. }
  195. /* set vi2c ops to their implementations */
  196. port_dev->vi2c.ops = &g_i2cmux_ops;
  197. /* save state variables */
  198. port_dev->dev = dev;
  199. port_dev->port = port;
  200. return &port_dev->vi2c;
  201. }
  202. /****************************************************************************
  203. * Name: pca9540bdp_initialize
  204. *
  205. * Description:
  206. * Initialize the PCA9540BDP device.
  207. *
  208. * Input Parameters:
  209. * i2c - An instance of the I2C interface to use to communicate with
  210. * PCA9540BDP
  211. * addr - The I2C address of the PCA9540BDP. The base I2C address of the
  212. * PCA9540BDP is 0x70.
  213. *
  214. * Returned Value:
  215. * Common i2c multiplexer device instance; NULL on failure.
  216. *
  217. ****************************************************************************/
  218. FAR struct pca9540bdp_dev_s *
  219. pca9540bdp_initialize(FAR struct i2c_master_s *i2c, uint8_t addr)
  220. {
  221. FAR struct pca9540bdp_dev_s *priv;
  222. /* Sanity check */
  223. DEBUGASSERT(i2c != NULL);
  224. /* Initialize the PCA9549BDP device structure */
  225. priv = (FAR struct pca9540bdp_dev_s *)
  226. kmm_malloc(sizeof(struct pca9540bdp_dev_s));
  227. if (priv == NULL)
  228. {
  229. i2cerr("ERROR: Failed to allocate instance\n");
  230. return NULL;
  231. }
  232. priv->i2c = i2c;
  233. priv->addr = addr;
  234. priv->state = 0x00;
  235. if (pca9540bdp_read_config(priv, &priv->state) != OK)
  236. {
  237. i2cerr("Could not read initial state from the device\n");
  238. kmm_free(priv);
  239. return NULL; /* signal error condition */
  240. }
  241. i2cinfo("Initial device state: %d\n", priv->state);
  242. if (pca9540bdp_select_port(priv, PCA9540BDP_SEL_PORT0) != OK)
  243. {
  244. i2cerr("Could not select mux port 0\n");
  245. kmm_free(priv);
  246. return NULL; /* signal error condition */
  247. }
  248. i2cinfo("PCA9549BDP (addr=0x%02x) set up with port %d\n", priv->addr,
  249. PCA9540BDP_SEL_PORT0);
  250. return priv;
  251. }
  252. #endif /* CONFIG_I2CMULTIPLEXER_PCA9540BDP */