pca9540bdp.c 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  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 <errno.h>
  43. #include <debug.h>
  44. #include <nuttx/kmalloc.h>
  45. #include <nuttx/fs/fs.h>
  46. #include <nuttx/i2c/i2c_master.h>
  47. #include "pca9540bdp.h"
  48. #include <nuttx/i2c/pca9540bdp.h>
  49. #ifdef CONFIG_I2CMULTIPLEXER_PCA9540BDP
  50. /****************************************************************************
  51. * Private Function Prototypes
  52. ****************************************************************************/
  53. /* I2C Helpers */
  54. static int pca9540bdp_write_config(FAR struct pca9540bdp_dev_s *priv,
  55. FAR uint8_t regvalue);
  56. static int pca9540bdp_read_config(FAR struct pca9540bdp_dev_s *priv,
  57. FAR uint8_t *regvalue);
  58. /* Other helpers */
  59. static int pca9540bdp_select_port(FAR struct pca9540bdp_dev_s *priv,
  60. uint8_t val);
  61. /* I2C multiplexer vtable */
  62. static int pca9540bdp_transfer_on_port (FAR struct i2c_master_s *dev,
  63. FAR struct i2c_msg_s *msgs, int count);
  64. #ifdef CONFIG_I2C_RESET
  65. static int pca9540bdp_reset_on_port (FAR struct i2c_master_s *dev);
  66. #endif
  67. /****************************************************************************
  68. * Private Data
  69. ****************************************************************************/
  70. static const struct i2c_ops_s g_i2cmux_ops =
  71. {
  72. pca9540bdp_transfer_on_port
  73. #ifdef CONFIG_I2C_RESET
  74. , pca9540bdp_reset_on_port
  75. #endif
  76. };
  77. /****************************************************************************
  78. * Private Functions
  79. ****************************************************************************/
  80. static int pca9540bdp_write_config(FAR struct pca9540bdp_dev_s *priv,
  81. FAR uint8_t regvalue)
  82. {
  83. struct i2c_config_s iconf;
  84. int ret;
  85. iconf.frequency = CONFIG_PCA9549BDP_I2C_FREQUENCY;
  86. iconf.address = priv->addr;
  87. iconf.addrlen = 7;
  88. ret = i2c_write(priv->i2c, &iconf, &regvalue, 1);
  89. i2cinfo("Write to address 0x%02X; register value: 0x%02x ret: %d\n",
  90. priv->addr, regvalue, ret);
  91. return ret;
  92. }
  93. static int pca9540bdp_read_config(FAR struct pca9540bdp_dev_s *priv,
  94. FAR uint8_t *regvalue)
  95. {
  96. struct i2c_config_s iconf;
  97. int ret;
  98. iconf.frequency = CONFIG_PCA9549BDP_I2C_FREQUENCY;
  99. iconf.address = priv->addr;
  100. iconf.addrlen = 7;
  101. ret = i2c_read(priv->i2c, &iconf, regvalue, 1);
  102. i2cinfo("Read from address: 0x%02X; register value: 0x%02x ret: %d\n",
  103. priv->addr, *regvalue, ret);
  104. return ret;
  105. }
  106. static int pca9540bdp_select_port(FAR struct pca9540bdp_dev_s *priv,
  107. uint8_t val)
  108. {
  109. if (val != PCA9540BDP_SEL_PORT0 && val != PCA9540BDP_SEL_PORT1)
  110. {
  111. /* port not supported */
  112. return -EINVAL;
  113. }
  114. if ((PCA9540BDP_ENABLE | val) == priv->state)
  115. {
  116. /* port already selected */
  117. return OK;
  118. }
  119. /* Modify state and write it to the mux */
  120. /* selecting a port always enables the device */
  121. priv->state = PCA9540BDP_ENABLE | val;
  122. return (pca9540bdp_write_config(priv, priv->state) == 0) ? OK : -ECOMM;
  123. }
  124. static int pca9540bdp_transfer_on_port(FAR struct i2c_master_s* dev,
  125. FAR struct i2c_msg_s *msgs, int count)
  126. {
  127. FAR struct i2c_port_dev_s* port_dev = (FAR struct i2c_port_dev_s*) dev;
  128. FAR struct pca9540bdp_dev_s* priv = port_dev->dev;
  129. int ret;
  130. /* select the mux port */
  131. if (pca9540bdp_select_port(priv, port_dev->port) != OK)
  132. {
  133. i2cerr("Could not select proper mux port\n");
  134. return -ECOMM; /* Signal error condition */
  135. }
  136. /* Resume the i2c transfer to the device connected to the mux port */
  137. ret = I2C_TRANSFER(priv->i2c, msgs, count);
  138. i2cinfo("Selected port %d and resumed transfer. Result: %d\n",
  139. port_dev->port, ret);
  140. return ret;
  141. }
  142. #ifdef CONFIG_I2C_RESET
  143. static int pca9540bdp_reset_on_port (FAR struct i2c_master_s *dev)
  144. {
  145. FAR struct i2c_port_dev_s* port_dev = (struct i2c_port_dev_s*) dev;
  146. FAR struct pca9540bdp_dev_s* priv = port_dev->dev;
  147. int port = port_dev->port;
  148. int ret;
  149. /* select the mux port */
  150. if (pca9540bdp_select_port(priv, port) != OK)
  151. {
  152. i2cerr("Could not select proper mux port\n");
  153. return -ECOMM; /* signal error condition */
  154. }
  155. /* resume the i2c reset for the device connected to the mux port */
  156. ret = I2C_RESET(priv->i2c);
  157. return ret;
  158. }
  159. #endif
  160. /****************************************************************************
  161. * Public Functions
  162. ****************************************************************************/
  163. /****************************************************************************
  164. * Name: pca9540bdp_lower_half
  165. *
  166. * Description:
  167. * Initialize the lower half of the PCA9540BDP by creating a i2c_master_s
  168. * for the virtual i2c master and link it to the associated PCA9540BDP and
  169. * its port.
  170. *
  171. * Input Parameters:
  172. * dev - Pointer to the associated PCA9540BDP
  173. * port - The port number as defined in pca9540bdp.h
  174. *
  175. * Returned Value:
  176. * Common i2c multiplexer device instance; NULL on failure.
  177. *
  178. ****************************************************************************/
  179. FAR struct i2c_master_s *
  180. pca9540bdp_lower_half(FAR struct pca9540bdp_dev_s *dev, uint8_t port)
  181. {
  182. FAR struct i2c_port_dev_s *port_dev;
  183. /* Sanity check */
  184. DEBUGASSERT(dev != NULL);
  185. /* Initialize the i2c_port_dev_s device structure */
  186. port_dev = (FAR struct i2c_port_dev_s *)
  187. kmm_malloc(sizeof(struct i2c_port_dev_s));
  188. if (port_dev == NULL)
  189. {
  190. i2cerr("ERROR: Failed to allocate i2c port lower half instance\n");
  191. return NULL;
  192. }
  193. /* set vi2c ops to their implementations */
  194. port_dev->vi2c.ops = &g_i2cmux_ops;
  195. /* save state variables */
  196. port_dev->dev = dev;
  197. port_dev->port = port;
  198. return &port_dev->vi2c;
  199. }
  200. /****************************************************************************
  201. * Name: pca9540bdp_initialize
  202. *
  203. * Description:
  204. * Initialize the PCA9540BDP device.
  205. *
  206. * Input Parameters:
  207. * i2c - An instance of the I2C interface to use to communicate with
  208. * PCA9540BDP
  209. * addr - The I2C address of the PCA9540BDP. The base I2C address of the
  210. * PCA9540BDP is 0x70.
  211. *
  212. * Returned Value:
  213. * Common i2c multiplexer device instance; NULL on failure.
  214. *
  215. ****************************************************************************/
  216. FAR struct pca9540bdp_dev_s *
  217. pca9540bdp_initialize(FAR struct i2c_master_s *i2c, uint8_t addr)
  218. {
  219. FAR struct pca9540bdp_dev_s *priv;
  220. /* Sanity check */
  221. DEBUGASSERT(i2c != NULL);
  222. /* Initialize the PCA9549BDP device structure */
  223. priv = (FAR struct pca9540bdp_dev_s *)
  224. kmm_malloc(sizeof(struct pca9540bdp_dev_s));
  225. if (priv == NULL)
  226. {
  227. i2cerr("ERROR: Failed to allocate instance\n");
  228. return NULL;
  229. }
  230. priv->i2c = i2c;
  231. priv->addr = addr;
  232. priv->state = 0x00;
  233. if (pca9540bdp_read_config(priv, &priv->state) != OK)
  234. {
  235. i2cerr("Could not read initial state from the device\n");
  236. kmm_free(priv);
  237. return NULL; /* signal error condition */
  238. }
  239. i2cinfo("Initial device state: %d\n", priv->state);
  240. if (pca9540bdp_select_port(priv, PCA9540BDP_SEL_PORT0) != OK)
  241. {
  242. i2cerr("Could not select mux port 0\n");
  243. kmm_free(priv);
  244. return NULL; /* signal error condition */
  245. }
  246. i2cinfo("PCA9549BDP (addr=0x%02x) set up with port %d\n", priv->addr, PCA9540BDP_SEL_PORT0);
  247. return priv;
  248. }
  249. #endif /* CONFIG_I2CMULTIPLEXER_PCA9540BDP */