tca64xx.c 40 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421
  1. /****************************************************************************
  2. * drivers/ioexpander/tca64xx.h
  3. * Supports the following parts: TCA6408, TCA6416, TCA6424
  4. *
  5. * Copyright (C) 2016-2017 Gregory Nutt. All rights reserved.
  6. * Author: Gregory Nutt <gnutt@nuttx.org>
  7. *
  8. * This header file derives, in part, from the Project Ara TCA64xx driver
  9. * which has this copyright:
  10. *
  11. * Copyright (c) 2014-2015 Google Inc.
  12. * All rights reserved.
  13. * Author: Patrick Titiano, Jean Pihet
  14. *
  15. * Redistribution and use in source and binary forms, with or without
  16. * modification, are permitted provided that the following conditions are met:
  17. *
  18. * 1. Redistributions of source code must retain the above copyright notice,
  19. * this list of conditions and the following disclaimer.
  20. * 2. Redistributions in binary form must reproduce the above copyright notice,
  21. * this list of conditions and the following disclaimer in the documentation
  22. * and/or other materials provided with the distribution.
  23. * 3. Neither the name of the copyright holder nor the names of its
  24. * contributors may be used to endorse or promote products derived from this
  25. * software without specific prior written permission.
  26. *
  27. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  28. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
  29. * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  30. * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
  31. * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  32. * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  33. * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
  34. * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  35. * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
  36. * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  37. * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  38. *
  39. ****************************************************************************/
  40. /****************************************************************************
  41. * Included Files
  42. ****************************************************************************/
  43. #include <nuttx/config.h>
  44. #include <semaphore.h>
  45. #include <assert.h>
  46. #include <errno.h>
  47. #include <debug.h>
  48. #include <nuttx/kmalloc.h>
  49. #include <nuttx/wdog.h>
  50. #include <nuttx/ioexpander/ioexpander.h>
  51. #include <nuttx/ioexpander/tca64xx.h>
  52. #include "tca64xx.h"
  53. #ifdef CONFIG_IOEXPANDER_TCA64XX
  54. /****************************************************************************
  55. * Pre-processor Definitions
  56. ****************************************************************************/
  57. #ifndef MAX
  58. # define MAX(a,b) (((a) > (b)) ? (a) : (b))
  59. #endif
  60. #ifndef MIN
  61. # define MIN(a,b) (((a) < (b)) ? (a) : (b))
  62. #endif
  63. /****************************************************************************
  64. * Private Function Prototypes
  65. ****************************************************************************/
  66. /* TCA64xx Helpers */
  67. static void tca64_lock(FAR struct tca64_dev_s *priv);
  68. static FAR const struct tca64_part_s *tca64_getpart(FAR struct tca64_dev_s *priv);
  69. static uint8_t tca64_ngpios(FAR struct tca64_dev_s *priv);
  70. static uint8_t tca64_input_reg(FAR struct tca64_dev_s *priv, uint8_t pin);
  71. static uint8_t tca64_output_reg(FAR struct tca64_dev_s *priv, uint8_t pin);
  72. static uint8_t tca64_polarity_reg(FAR struct tca64_dev_s *priv, uint8_t pin);
  73. static uint8_t tca64_config_reg(FAR struct tca64_dev_s *priv, uint8_t pin);
  74. static int tca64_getreg(FAR struct tca64_dev_s *priv, uint8_t regaddr,
  75. FAR uint8_t *regval, unsigned int count);
  76. static int tca64_putreg(struct tca64_dev_s *priv, uint8_t regaddr,
  77. FAR uint8_t *regval, unsigned int count);
  78. /* I/O Expander Methods */
  79. static int tca64_direction(FAR struct ioexpander_dev_s *dev, uint8_t pin,
  80. int dir);
  81. static int tca64_option(FAR struct ioexpander_dev_s *dev, uint8_t pin,
  82. int opt, void *regval);
  83. static int tca64_writepin(FAR struct ioexpander_dev_s *dev, uint8_t pin,
  84. bool value);
  85. static int tca64_readpin(FAR struct ioexpander_dev_s *dev, uint8_t pin,
  86. FAR bool *value);
  87. #ifdef CONFIG_IOEXPANDER_MULTIPIN
  88. static int tca64_multiwritepin(FAR struct ioexpander_dev_s *dev,
  89. FAR uint8_t *pins, FAR bool *values, int count);
  90. static int tca64_multireadpin(FAR struct ioexpander_dev_s *dev,
  91. FAR uint8_t *pins, FAR bool *values, int count);
  92. #endif
  93. #ifdef CONFIG_IOEXPANDER_INT_ENABLE
  94. static FAR void *tca64_attach(FAR struct ioexpander_dev_s *dev,
  95. ioe_pinset_t pinset, ioe_callback_t callback, FAR void *arg);
  96. static int tca64_detach(FAR struct ioexpander_dev_s *dev, FAR void *handle);
  97. #endif
  98. #ifdef CONFIG_TCA64XX_INT_ENABLE
  99. static void tca64_int_update(FAR struct tca64_dev_s *priv,
  100. ioe_pinset_t input, ioe_pinset_t mask);
  101. static void tca64_register_update(FAR struct tca64_dev_s *priv);
  102. static void tca64_irqworker(void *arg);
  103. static void tca64_interrupt(FAR void *arg);
  104. #ifdef CONFIG_TCA64XX_INT_POLL
  105. static void tca64_poll_expiry(int argc, wdparm_t arg1, ...);
  106. #endif
  107. #endif
  108. /****************************************************************************
  109. * Private Data
  110. ****************************************************************************/
  111. #ifndef CONFIG_TCA64XX_MULTIPLE
  112. /* If only a single device is supported, then the driver state structure may
  113. * as well be pre-allocated.
  114. */
  115. static struct tca64_dev_s g_tca64;
  116. #endif
  117. /* I/O expander vtable */
  118. static const struct ioexpander_ops_s g_tca64_ops =
  119. {
  120. tca64_direction,
  121. tca64_option,
  122. tca64_writepin,
  123. tca64_readpin,
  124. tca64_readpin
  125. #ifdef CONFIG_IOEXPANDER_MULTIPIN
  126. , tca64_multiwritepin
  127. , tca64_multireadpin
  128. , tca64_multireadpin
  129. #endif
  130. #ifdef CONFIG_IOEXPANDER_INT_ENABLE
  131. , tca64_attach
  132. , tca64_detach
  133. #endif
  134. };
  135. /* TCA64 part data */
  136. static const struct tca64_part_s g_tca64_parts[TCA64_NPARTS] =
  137. {
  138. {
  139. TCA6408_PART,
  140. MIN(TCA6408_NR_GPIOS, CONFIG_IOEXPANDER_NPINS),
  141. TCA6408_INPUT_REG,
  142. TCA6408_OUTPUT_REG,
  143. TCA6408_POLARITY_REG,
  144. TCA6408_CONFIG_REG,
  145. },
  146. {
  147. TCA6416_PART,
  148. MIN(TCA6416_NR_GPIOS, CONFIG_IOEXPANDER_NPINS),
  149. TCA6416_INPUT0_REG,
  150. TCA6416_OUTPUT0_REG,
  151. TCA6416_POLARITY0_REG,
  152. TCA6416_CONFIG0_REG,
  153. },
  154. {
  155. TCA6424_PART,
  156. MIN(TCA6424_NR_GPIOS, CONFIG_IOEXPANDER_NPINS),
  157. TCA6424_INPUT0_REG,
  158. TCA6424_OUTPUT0_REG,
  159. TCA6424_POLARITY0_REG,
  160. TCA6424_CONFIG0_REG,
  161. },
  162. };
  163. /****************************************************************************
  164. * Private Functions
  165. ****************************************************************************/
  166. /****************************************************************************
  167. * Name: tca64_lock
  168. *
  169. * Description:
  170. * Get exclusive access to the I/O Expander
  171. *
  172. ****************************************************************************/
  173. static void tca64_lock(FAR struct tca64_dev_s *priv)
  174. {
  175. int ret;
  176. do
  177. {
  178. /* Take the semaphore (perhaps waiting) */
  179. ret = nxsem_wait(&priv->exclsem);
  180. /* The only case that an error should occur here is if the wait was
  181. * awakened by a signal.
  182. */
  183. DEBUGASSERT(ret == OK || ret == -EINTR);
  184. }
  185. while (ret == -EINTR);
  186. }
  187. #define tca64_unlock(p) nxsem_post(&(p)->exclsem)
  188. /****************************************************************************
  189. * Name: tca64_getpart
  190. *
  191. * Description:
  192. * Look up information for the selected part
  193. *
  194. ****************************************************************************/
  195. static FAR const struct tca64_part_s *tca64_getpart(FAR struct tca64_dev_s *priv)
  196. {
  197. DEBUGASSERT(priv != NULL && priv->config != NULL &&
  198. priv->config->part < TCA64_NPARTS);
  199. return &g_tca64_parts[priv->config->part];
  200. }
  201. /****************************************************************************
  202. * Name: tca64_ngpios
  203. *
  204. * Description:
  205. * Return the number of GPIOs supported by the selected part
  206. *
  207. ****************************************************************************/
  208. static uint8_t tca64_ngpios(FAR struct tca64_dev_s *priv)
  209. {
  210. FAR const struct tca64_part_s *part = tca64_getpart(priv);
  211. return part->tp_ngpios;
  212. }
  213. /****************************************************************************
  214. * Name: tca64_input_reg
  215. *
  216. * Description:
  217. * Return the address of the input register for the specified pin.
  218. *
  219. ****************************************************************************/
  220. static uint8_t tca64_input_reg(FAR struct tca64_dev_s *priv, uint8_t pin)
  221. {
  222. FAR const struct tca64_part_s *part = tca64_getpart(priv);
  223. uint8_t reg = part->tp_input;
  224. DEBUGASSERT(pin <= part->tp_ngpios);
  225. return reg + (pin >> 3);
  226. }
  227. /****************************************************************************
  228. * Name: tca64_output_reg
  229. *
  230. * Description:
  231. * Return the address of the output register for the specified pin.
  232. *
  233. ****************************************************************************/
  234. static uint8_t tca64_output_reg(FAR struct tca64_dev_s *priv, uint8_t pin)
  235. {
  236. FAR const struct tca64_part_s *part = tca64_getpart(priv);
  237. uint8_t reg = part->tp_output;
  238. DEBUGASSERT(pin <= part->tp_ngpios);
  239. return reg + (pin >> 3);
  240. }
  241. /****************************************************************************
  242. * Name: tca64_polarity_reg
  243. *
  244. * Description:
  245. * Return the address of the polarity register for the specified pin.
  246. *
  247. ****************************************************************************/
  248. static uint8_t tca64_polarity_reg(FAR struct tca64_dev_s *priv, uint8_t pin)
  249. {
  250. FAR const struct tca64_part_s *part = tca64_getpart(priv);
  251. uint8_t reg = part->tp_output;
  252. DEBUGASSERT(pin <= part->tp_ngpios);
  253. return reg + (pin >> 3);
  254. }
  255. /****************************************************************************
  256. * Name: tca64_config_reg
  257. *
  258. * Description:
  259. * Return the address of the configuration register for the specified pin.
  260. *
  261. ****************************************************************************/
  262. static uint8_t tca64_config_reg(FAR struct tca64_dev_s *priv, uint8_t pin)
  263. {
  264. FAR const struct tca64_part_s *part = tca64_getpart(priv);
  265. uint8_t reg = part->tp_config;
  266. DEBUGASSERT(pin <= part->tp_ngpios);
  267. return reg + (pin >> 3);
  268. }
  269. /****************************************************************************
  270. * Name: tca64_getreg
  271. *
  272. * Description:
  273. * Read an 8-bit value from a TCA64xx register
  274. *
  275. ****************************************************************************/
  276. static int tca64_getreg(FAR struct tca64_dev_s *priv, uint8_t regaddr,
  277. FAR uint8_t *regval, unsigned int count)
  278. {
  279. struct i2c_msg_s msg[2];
  280. int ret;
  281. DEBUGASSERT(priv != NULL && priv->i2c != NULL && priv->config != NULL);
  282. /* Set up for the transfer */
  283. msg[0].frequency = TCA64XX_I2C_MAXFREQUENCY,
  284. msg[0].addr = priv->config->address,
  285. msg[0].flags = 0,
  286. msg[0].buffer = &regaddr,
  287. msg[0].length = 1,
  288. msg[1].frequency = TCA64XX_I2C_MAXFREQUENCY,
  289. msg[1].addr = priv->config->address,
  290. msg[1].flags = I2C_M_READ,
  291. msg[1].buffer = regval,
  292. msg[1].length = count,
  293. /* Perform the transfer */
  294. ret = I2C_TRANSFER(priv->i2c, msg, 2);
  295. if (ret < 0)
  296. {
  297. gpioerr("ERROR: I2C addr=%02x regaddr=%02x: failed, ret=%d!\n",
  298. priv->config->address, regaddr, ret);
  299. return ret;
  300. }
  301. else
  302. {
  303. gpioinfo("I2C addr=%02x regaddr=%02x: read %02x\n",
  304. priv->config->address, regaddr, *regval);
  305. return OK;
  306. }
  307. }
  308. /****************************************************************************
  309. * Name: tca64_putreg
  310. *
  311. * Description:
  312. * Write an 8-bit value to a TCA64xx register
  313. *
  314. ****************************************************************************/
  315. static int tca64_putreg(struct tca64_dev_s *priv, uint8_t regaddr,
  316. FAR uint8_t *regval, unsigned int count)
  317. {
  318. struct i2c_msg_s msg[1];
  319. uint8_t cmd[2];
  320. int ret;
  321. int i;
  322. DEBUGASSERT(priv != NULL && priv->i2c != NULL && priv->config != NULL);
  323. /* Set up for the transfer */
  324. cmd[0] = regaddr;
  325. for (i = 0; i < count; i++)
  326. {
  327. cmd[i+1] = regval[i];
  328. }
  329. msg[0].frequency = TCA64XX_I2C_MAXFREQUENCY,
  330. msg[0].addr = priv->config->address,
  331. msg[0].flags = 0,
  332. msg[0].buffer = cmd,
  333. msg[0].length = count + 1,
  334. ret = I2C_TRANSFER(priv->i2c, msg, 1);
  335. if (ret < 0)
  336. {
  337. gpioerr("ERROR: claddr=%02x, regaddr=%02x: failed, ret=%d!\n",
  338. priv->config->address, regaddr, ret);
  339. return ret;
  340. }
  341. else
  342. {
  343. gpioinfo("claddr=%02x, regaddr=%02x, regval=%02x\n",
  344. priv->config->address, regaddr, regval);
  345. return OK;
  346. }
  347. }
  348. /****************************************************************************
  349. * Name: tca64_direction
  350. *
  351. * Description:
  352. * Set the direction of an ioexpander pin. Required.
  353. *
  354. * Input Parameters:
  355. * dev - Device-specific state data
  356. * pin - The index of the pin to alter in this call
  357. * dir - One of the IOEXPANDER_DIRECTION_ macros
  358. *
  359. * Returned Value:
  360. * 0 on success, else a negative error code
  361. *
  362. ****************************************************************************/
  363. static int tca64_direction(FAR struct ioexpander_dev_s *dev, uint8_t pin,
  364. int direction)
  365. {
  366. FAR struct tca64_dev_s *priv = (FAR struct tca64_dev_s *)dev;
  367. uint8_t regaddr;
  368. uint8_t regval;
  369. int ret;
  370. DEBUGASSERT(priv != NULL && priv->config != NULL &&
  371. pin < CONFIG_IOEXPANDER_NPINS &&
  372. (direction == IOEXPANDER_DIRECTION_IN ||
  373. direction == IOEXPANDER_DIRECTION_OUT));
  374. gpioinfo("I2C addr=%02x pin=%u direction=%s\n",
  375. priv->config->address, pin,
  376. (direction == IOEXPANDER_DIRECTION_IN) ? "IN" : "OUT");
  377. /* Get exclusive access to the I/O Expander */
  378. tca64_lock(priv);
  379. /* Read the Configuration Register associated with this pin. The
  380. * Configuration Register configures the direction of the I/O pins.
  381. */
  382. regaddr = tca64_config_reg(priv, pin);
  383. ret = tca64_getreg(priv, regaddr, &regval, 1);
  384. if (ret < 0)
  385. {
  386. gpioerr("ERROR: Failed to read config register at %u: %d\n",
  387. regaddr, ret);
  388. goto errout_with_lock;
  389. }
  390. /* Set the pin direction in the I/O Expander */
  391. if (direction == IOEXPANDER_DIRECTION_IN)
  392. {
  393. /* Configure pin as input. If a bit in the configuration register is
  394. * set to 1, the corresponding port pin is enabled as an input with a
  395. * high-impedance output driver.
  396. */
  397. regval |= (1 << (pin & 7));
  398. }
  399. else /* if (direction == IOEXPANDER_DIRECTION_OUT) */
  400. {
  401. /* Configure pin as output. If a bit in this register is cleared to
  402. * 0, the corresponding port pin is enabled as an output.
  403. *
  404. * REVISIT: The value of output has not been selected! This might
  405. * put a glitch on the output.
  406. */
  407. regval &= ~(1 << (pin & 7));
  408. }
  409. /* Write back the modified register content */
  410. ret = tca64_putreg(priv, regaddr, &regval, 1);
  411. if (ret < 0)
  412. {
  413. gpioerr("ERROR: Failed to write config register at %u: %d\n",
  414. regaddr, ret);
  415. }
  416. errout_with_lock:
  417. tca64_unlock(priv);
  418. return ret;
  419. }
  420. /****************************************************************************
  421. * Name: tca64_option
  422. *
  423. * Description:
  424. * Set pin options. Required.
  425. * Since all IO expanders have various pin options, this API allows setting
  426. * pin options in a flexible way.
  427. *
  428. * Input Parameters:
  429. * dev - Device-specific state data
  430. * pin - The index of the pin to alter in this call
  431. * opt - One of the IOEXPANDER_OPTION_ macros
  432. * val - The option's value
  433. *
  434. * Returned Value:
  435. * 0 on success, else a negative error code
  436. *
  437. ****************************************************************************/
  438. static int tca64_option(FAR struct ioexpander_dev_s *dev, uint8_t pin,
  439. int opt, FAR void *value)
  440. {
  441. FAR struct tca64_dev_s *priv = (FAR struct tca64_dev_s *)dev;
  442. int ret = -ENOSYS;
  443. DEBUGASSERT(priv != NULL && priv->config != NULL);
  444. gpioinfo("I2C addr=%02x pin=%u option=%u\n",
  445. priv->config->address, pin, opt);
  446. /* Check for pin polarity inversion. The Polarity Inversion Register
  447. * allows polarity inversion of pins defined as inputs by the
  448. * Configuration Register. If a bit in this register is set, the
  449. * corresponding port pin's polarity is inverted. If a bit in this
  450. * register is cleared, the corresponding port pin's original polarity
  451. * is retained.
  452. */
  453. if (opt == IOEXPANDER_OPTION_INVERT)
  454. {
  455. unsigned int ival = (unsigned int)((uintptr_t)value);
  456. uint8_t regaddr;
  457. uint8_t polarity;
  458. /* Get exclusive access to the I/O Expander */
  459. tca64_lock(priv);
  460. /* Read the polarity register */
  461. regaddr = tca64_polarity_reg(priv, pin);
  462. ret = tca64_getreg(priv, regaddr, &polarity, 1);
  463. if (ret < 0)
  464. {
  465. gpioerr("ERROR: Failed to read polarity register at %u: %d\n",
  466. regaddr, ret);
  467. tca64_unlock(priv);
  468. return ret;
  469. }
  470. /* Set/clear the pin option */
  471. if (ival == IOEXPANDER_OPTION_INVERT)
  472. {
  473. polarity |= (1 << (pin & 7));
  474. }
  475. else
  476. {
  477. polarity &= ~(1 << (pin & 7));
  478. }
  479. /* Write back the modified register */
  480. ret = tca64_putreg(priv, regaddr, &polarity, 1);
  481. if (ret < 0)
  482. {
  483. gpioerr("ERROR: Failed to read polarity register at %u: %d\n",
  484. regaddr, ret);
  485. }
  486. tca64_unlock(priv);
  487. }
  488. #ifdef CONFIG_TCA64XX_INT_ENABLE
  489. /* Interrupt configuration */
  490. else if (opt == IOEXPANDER_OPTION_INTCFG)
  491. {
  492. unsigned int ival = (unsigned int)((uintptr_t)value);
  493. ioe_pinset_t bit = ((ioe_pinset_t)1 << pin);
  494. ret = OK;
  495. tca64_lock(priv);
  496. switch (ival)
  497. {
  498. case IOEXPANDER_VAL_HIGH: /* Interrupt on high level */
  499. priv->trigger &= ~bit;
  500. priv->level[0] |= bit;
  501. priv->level[1] &= ~bit;
  502. break;
  503. case IOEXPANDER_VAL_LOW: /* Interrupt on low level */
  504. priv->trigger &= ~bit;
  505. priv->level[0] &= ~bit;
  506. priv->level[1] |= bit;
  507. break;
  508. case IOEXPANDER_VAL_RISING: /* Interrupt on rising edge */
  509. priv->trigger |= bit;
  510. priv->level[0] |= bit;
  511. priv->level[1] &= ~bit;
  512. break;
  513. case IOEXPANDER_VAL_FALLING: /* Interrupt on falling edge */
  514. priv->trigger |= bit;
  515. priv->level[0] &= ~bit;
  516. priv->level[1] |= bit;
  517. break;
  518. case IOEXPANDER_VAL_BOTH: /* Interrupt on both edges */
  519. priv->trigger |= bit;
  520. priv->level[0] |= bit;
  521. priv->level[1] |= bit;
  522. break;
  523. case IOEXPANDER_VAL_DISABLE:
  524. break;
  525. default:
  526. ret = -EINVAL;
  527. }
  528. tca64_unlock(priv);
  529. }
  530. #endif
  531. return ret;
  532. }
  533. /****************************************************************************
  534. * Name: tca64_writepin
  535. *
  536. * Description:
  537. * Set the pin level. Required.
  538. *
  539. * Input Parameters:
  540. * dev - Device-specific state data
  541. * pin - The index of the pin to alter in this call
  542. * val - The pin level. Usually TRUE will set the pin high,
  543. * except if OPTION_INVERT has been set on this pin.
  544. *
  545. * Returned Value:
  546. * 0 on success, else a negative error code
  547. *
  548. ****************************************************************************/
  549. static int tca64_writepin(FAR struct ioexpander_dev_s *dev, uint8_t pin,
  550. bool value)
  551. {
  552. FAR struct tca64_dev_s *priv = (FAR struct tca64_dev_s *)dev;
  553. uint8_t regaddr;
  554. uint8_t regval;
  555. int ret;
  556. DEBUGASSERT(priv != NULL && priv->config != NULL &&
  557. pin < CONFIG_IOEXPANDER_NPINS);
  558. gpioinfo("I2C addr=%02x pin=%u value=%u\n",
  559. priv->config->address, pin, value);
  560. /* Get exclusive access to the I/O Expander */
  561. tca64_lock(priv);
  562. /* Read the output register. */
  563. regaddr = tca64_output_reg(priv, pin);
  564. ret = tca64_getreg(priv, regaddr, &regval, 1);
  565. if (ret < 0)
  566. {
  567. gpioerr("ERROR: Failed to read output register at %u: %d\n",
  568. regaddr, ret);
  569. goto errout_with_lock;
  570. }
  571. /* Set output pins default value (before configuring it as output) The
  572. * Output Port Register shows the outgoing logic levels of the pins
  573. * defined as outputs by the Configuration Register.
  574. */
  575. if (value != 0)
  576. {
  577. regval |= (1 << (pin & 7));
  578. }
  579. else
  580. {
  581. regval &= ~(1 << (pin & 7));
  582. }
  583. /* Write the modified output register value */
  584. ret = tca64_putreg(priv, regaddr, &regval, 1);
  585. if (ret < 0)
  586. {
  587. gpioerr("ERROR: Failed to write output register at %u: %d\n",
  588. regaddr, ret);
  589. }
  590. errout_with_lock:
  591. tca64_unlock(priv);
  592. return ret;
  593. }
  594. /****************************************************************************
  595. * Name: tca64_readpin
  596. *
  597. * Description:
  598. * Read the actual PIN level. This can be different from the last value written
  599. * to this pin. Required.
  600. *
  601. * Input Parameters:
  602. * dev - Device-specific state data
  603. * pin - The index of the pin
  604. * valptr - Pointer to a buffer where the pin level is stored. Usually TRUE
  605. * if the pin is high, except if OPTION_INVERT has been set on this pin.
  606. *
  607. * Returned Value:
  608. * 0 on success, else a negative error code
  609. *
  610. ****************************************************************************/
  611. static int tca64_readpin(FAR struct ioexpander_dev_s *dev, uint8_t pin,
  612. FAR bool *value)
  613. {
  614. FAR struct tca64_dev_s *priv = (FAR struct tca64_dev_s *)dev;
  615. uint8_t regaddr;
  616. uint8_t regval;
  617. int ret;
  618. DEBUGASSERT(priv != NULL && priv->config != NULL &&
  619. pin < CONFIG_IOEXPANDER_NPINS && value != NULL);
  620. gpioinfo("I2C addr=%02x, pin=%u\n", priv->config->address, pin);
  621. /* Get exclusive access to the I/O Expander */
  622. tca64_lock(priv);
  623. /* Read the input register for this pin
  624. *
  625. * The Input Port Register reflects the incoming logic levels of the pins,
  626. * regardless of whether the pin is defined as an input or an output by
  627. * the Configuration Register. They act only on read operation.
  628. */
  629. regaddr = tca64_input_reg(priv, pin);
  630. ret = tca64_getreg(priv, regaddr, &regval, 1);
  631. if (ret < 0)
  632. {
  633. gpioerr("ERROR: Failed to read input register at %u: %d\n",
  634. regaddr, ret);
  635. goto errout_with_lock;
  636. }
  637. #ifdef CONFIG_TCA64XX_INT_ENABLE
  638. /* Update the input status with the 8 bits read from the expander */
  639. tca64_int_update(priv, (ioe_pinset_t)regval << (pin & ~7),
  640. (ioe_pinset_t)0xff << (pin & ~7));
  641. #endif
  642. /* Return 0 or 1 to indicate the state of pin */
  643. *value = (bool)((regval >> (pin & 7)) & 1);
  644. ret = OK;
  645. errout_with_lock:
  646. tca64_unlock(priv);
  647. return ret;
  648. }
  649. /****************************************************************************
  650. * Name: tca64_multiwritepin
  651. *
  652. * Description:
  653. * Set the pin level for multiple pins. This routine may be faster than
  654. * individual pin accesses. Optional.
  655. *
  656. * Input Parameters:
  657. * dev - Device-specific state data
  658. * pins - The list of pin indexes to alter in this call
  659. * val - The list of pin levels.
  660. *
  661. * Returned Value:
  662. * 0 on success, else a negative error code
  663. *
  664. ****************************************************************************/
  665. #ifdef CONFIG_IOEXPANDER_MULTIPIN
  666. static int tca64_multiwritepin(FAR struct ioexpander_dev_s *dev,
  667. FAR uint8_t *pins, FAR bool *values,
  668. int count)
  669. {
  670. FAR struct tca64_dev_s *priv = (FAR struct tca64_dev_s *)dev;
  671. ioe_pinset_t pinset;
  672. uint8_t regaddr;
  673. uint8_t ngpios;
  674. uint8_t nregs;
  675. uint8_t pin;
  676. int ret;
  677. int i;
  678. /* Get exclusive access to the I/O Expander */
  679. tca64_lock(priv);
  680. /* Read the output registers for pin 0 through the number of supported
  681. * pins.
  682. */
  683. ngpios = tca64_ngpios(priv);
  684. nregs = (ngpios + 7) >> 3;
  685. pinset = 0;
  686. regaddr = tca64_output_reg(priv, 0);
  687. ret = tca64_getreg(priv, regaddr, (FAR uint8_t *)&pinset, nregs);
  688. if (ret < 0)
  689. {
  690. gpioerr("ERROR: Failed to read %u output registers at %u: %d\n",
  691. nregs, regaddr, ret);
  692. goto errout_with_lock;
  693. }
  694. /* Apply the user defined changes */
  695. for (i = 0; i < count; i++)
  696. {
  697. pin = pins[i];
  698. DEBUGASSERT(pin < CONFIG_IOEXPANDER_NPINS);
  699. if (values[i])
  700. {
  701. pinset |= (1 << pin);
  702. }
  703. else
  704. {
  705. pinset &= ~(1 << pin);
  706. }
  707. }
  708. /* Now write back the new pins states */
  709. ret = tca64_putreg(priv, regaddr, (FAR uint8_t *)&pinset, nregs);
  710. if (ret < 0)
  711. {
  712. gpioerr("ERROR: Failed to write %u output registers at %u: %d\n",
  713. nregs, regaddr, ret);
  714. }
  715. errout_with_lock:
  716. tca64_unlock(priv);
  717. return ret;
  718. }
  719. #endif
  720. /****************************************************************************
  721. * Name: tca64_multireadpin
  722. *
  723. * Description:
  724. * Read the actual level for multiple pins. This routine may be faster than
  725. * individual pin accesses. Optional.
  726. *
  727. * Input Parameters:
  728. * dev - Device-specific state data
  729. * pin - The list of pin indexes to read
  730. * valptr - Pointer to a buffer where the pin levels are stored.
  731. *
  732. * Returned Value:
  733. * 0 on success, else a negative error code
  734. *
  735. ****************************************************************************/
  736. #ifdef CONFIG_IOEXPANDER_MULTIPIN
  737. static int tca64_multireadpin(FAR struct ioexpander_dev_s *dev,
  738. FAR uint8_t *pins, FAR bool *values,
  739. int count)
  740. {
  741. FAR struct tca64_dev_s *priv = (FAR struct tca64_dev_s *)dev;
  742. ioe_pinset_t pinset;
  743. uint8_t regaddr;
  744. uint8_t ngpios;
  745. uint8_t nregs;
  746. uint8_t pin;
  747. int ret;
  748. int i;
  749. DEBUGASSERT(priv != NULL && priv->config != NULL && pins != NULL &&
  750. values != NULL && count > 0);
  751. gpioinfo("I2C addr=%02x, count=%u\n", priv->config->address, count);
  752. /* Get exclusive access to the I/O Expander */
  753. tca64_lock(priv);
  754. /* Read the input register for pin 0 through the number of supported pins.
  755. *
  756. * The Input Port Register reflects the incoming logic levels of the pins,
  757. * regardless of whether the pin is defined as an input or an output by
  758. * the Configuration Register. They act only on read operation.
  759. */
  760. ngpios = tca64_ngpios(priv);
  761. nregs = (ngpios + 7) >> 3;
  762. pinset = 0;
  763. regaddr = tca64_input_reg(priv, 0);
  764. ret = tca64_getreg(priv, regaddr, (FAR uint8_t *)&pinset, nregs);
  765. if (ret < 0)
  766. {
  767. gpioerr("ERROR: Failed to read input %u registers at %u: %d\n",
  768. nregs, regaddr, ret);
  769. goto errout_with_lock;
  770. }
  771. /* Update the input status with the 8 bits read from the expander */
  772. for (i = 0; i < count; i++)
  773. {
  774. pin = pins[i];
  775. DEBUGASSERT(pin < CONFIG_IOEXPANDER_NPINS);
  776. values[i] = ((pinset & (1 << pin)) != 0);
  777. }
  778. #ifdef CONFIG_TCA64XX_INT_ENABLE
  779. /* Update the input status with the 32 bits read from the expander */
  780. tca64_int_update(priv, pinset, PINSET_ALL);
  781. #endif
  782. errout_with_lock:
  783. tca64_unlock(priv);
  784. return ret;
  785. }
  786. #endif
  787. /****************************************************************************
  788. * Name: tca64_attach
  789. *
  790. * Description:
  791. * Attach and enable a pin interrupt callback function.
  792. *
  793. * Input Parameters:
  794. * dev - Device-specific state data
  795. * pinset - The set of pin events that will generate the callback
  796. * callback - The pointer to callback function. NULL will detach the
  797. * callback.
  798. * arg - User-provided callback argument
  799. *
  800. * Returned Value:
  801. * A non-NULL handle value is returned on success. This handle may be
  802. * used later to detach and disable the pin interrupt.
  803. *
  804. ****************************************************************************/
  805. #ifdef CONFIG_TCA64XX_INT_ENABLE
  806. static FAR void *tca64_attach(FAR struct ioexpander_dev_s *dev,
  807. ioe_pinset_t pinset, ioe_callback_t callback,
  808. FAR void *arg)
  809. {
  810. FAR struct tca64_dev_s *priv = (FAR struct tca64_dev_s *)dev;
  811. FAR void *handle = NULL;
  812. int i;
  813. /* Get exclusive access to the I/O Expander */
  814. tca64_lock(priv);
  815. /* Find and available in entry in the callback table */
  816. for (i = 0; i < CONFIG_TCA64XX_INT_NCALLBACKS; i++)
  817. {
  818. /* Is this entry available (i.e., no callback attached) */
  819. if (priv->cb[i].cbfunc == NULL)
  820. {
  821. /* Yes.. use this entry */
  822. priv->cb[i].pinset = pinset;
  823. priv->cb[i].cbfunc = callback;
  824. priv->cb[i].cbarg = arg;
  825. handle = &priv->cb[i];
  826. break;
  827. }
  828. }
  829. tca64_unlock(priv);
  830. return handle;
  831. }
  832. /****************************************************************************
  833. * Name: tca64_detach
  834. *
  835. * Description:
  836. * Detach and disable a pin interrupt callback function.
  837. *
  838. * Input Parameters:
  839. * dev - Device-specific state data
  840. * handle - The non-NULL opaque value return by tca64_attch()
  841. *
  842. * Returned Value:
  843. * 0 on success, else a negative error code
  844. *
  845. ****************************************************************************/
  846. static int tca64_detach(FAR struct ioexpander_dev_s *dev, FAR void *handle)
  847. {
  848. FAR struct tca64_dev_s *priv = (FAR struct tca64_dev_s *)dev;
  849. FAR struct tca64_callback_s *cb = (FAR struct tca64_callback_s *)handle;
  850. DEBUGASSERT(priv != NULL && cb != NULL);
  851. DEBUGASSERT((uintptr_t)cb >= (uintptr_t)&priv->cb[0] &&
  852. (uintptr_t)cb <= (uintptr_t)&priv->cb[CONFIG_TCA64XX_INT_NCALLBACKS-1]);
  853. UNUSED(priv);
  854. cb->pinset = 0;
  855. cb->cbfunc = NULL;
  856. cb->cbarg = NULL;
  857. return OK;
  858. }
  859. #endif
  860. /****************************************************************************
  861. * Name: tca64_int_update
  862. *
  863. * Description:
  864. * Check for pending interrupts.
  865. *
  866. ****************************************************************************/
  867. #ifdef CONFIG_TCA64XX_INT_ENABLE
  868. static void tca64_int_update(FAR struct tca64_dev_s *priv, ioe_pinset_t input,
  869. ioe_pinset_t mask)
  870. {
  871. ioe_pinset_t diff;
  872. irqstate_t flags;
  873. int ngios = tca64_ngpios(priv);
  874. int pin;
  875. flags = enter_critical_section();
  876. /* Check the changed bits from last read */
  877. input = (priv->input & ~mask) | (input & mask);
  878. diff = priv->input ^ input;
  879. priv->input = input;
  880. /* TCA64XX doesn't support irq trigger, we have to do this in software. */
  881. for (pin = 0; pin < ngios; pin++)
  882. {
  883. if (TCA64_EDGE_SENSITIVE(priv, pin))
  884. {
  885. /* Edge triggered. Was there a change in the level? */
  886. if ((diff & 1) != 0)
  887. {
  888. /* Set interrupt as a function of edge type */
  889. if (((input & 1) == 0 && TCA64_EDGE_FALLING(priv, pin)) ||
  890. ((input & 1) != 0 && TCA64_EDGE_RISING(priv, pin)))
  891. {
  892. priv->intstat |= 1 << pin;
  893. }
  894. }
  895. }
  896. else /* if (TCA64_LEVEL_SENSITIVE(priv, pin)) */
  897. {
  898. /* Level triggered. Set intstat bit if match in level type. */
  899. if (((input & 1) != 0 && TCA64_LEVEL_HIGH(priv, pin)) ||
  900. ((input & 1) == 0 && TCA64_LEVEL_LOW(priv, pin)))
  901. {
  902. priv->intstat |= 1 << pin;
  903. }
  904. }
  905. diff >>= 1;
  906. input >>= 1;
  907. }
  908. leave_critical_section(flags);
  909. }
  910. #endif
  911. /****************************************************************************
  912. * Name: tc64_update_registers
  913. *
  914. * Description:
  915. * Read all pin states and update pending interrupts.
  916. *
  917. * Input Parameters:
  918. * dev - Device-specific state data
  919. * pins - The list of pin indexes to alter in this call
  920. * val - The list of pin levels.
  921. *
  922. * Returned Value:
  923. * 0 on success, else a negative error code
  924. *
  925. ****************************************************************************/
  926. #ifdef CONFIG_TCA64XX_INT_ENABLE
  927. static void tca64_register_update(FAR struct tca64_dev_s *priv)
  928. {
  929. ioe_pinset_t pinset;
  930. uint8_t regaddr;
  931. uint8_t ngpios;
  932. uint8_t nregs;
  933. int ret;
  934. /* Read the input register for pin 0 through the number of supported pins.
  935. *
  936. * The Input Port Register reflects the incoming logic levels of the pins,
  937. * regardless of whether the pin is defined as an input or an output by
  938. * the Configuration Register. They act only on read operation.
  939. */
  940. ngpios = tca64_ngpios(priv);
  941. nregs = (ngpios + 7) >> 3;
  942. pinset = 0;
  943. regaddr = tca64_input_reg(priv, 0);
  944. ret = tca64_getreg(priv, regaddr, (FAR uint8_t *)&pinset, nregs);
  945. if (ret < 0)
  946. {
  947. gpioerr("ERROR: Failed to read input %u registers at %u: %d\n",
  948. nregs, regaddr, ret);
  949. return;
  950. }
  951. /* Update the input status with the 32 bits read from the expander */
  952. tca64_int_update(priv, pinset, PINSET_ALL);
  953. }
  954. #endif
  955. /****************************************************************************
  956. * Name: tca64_irqworker
  957. *
  958. * Description:
  959. * Handle GPIO interrupt events (this function actually executes in the
  960. * context of the worker thread).
  961. *
  962. ****************************************************************************/
  963. #ifdef CONFIG_TCA64XX_INT_ENABLE
  964. static void tca64_irqworker(void *arg)
  965. {
  966. FAR struct tca64_dev_s *priv = (FAR struct tca64_dev_s *)arg;
  967. ioe_pinset_t pinset;
  968. uint8_t regaddr;
  969. uint8_t ngpios;
  970. uint8_t nregs;
  971. int ret;
  972. int i;
  973. DEBUGASSERT(priv != NULL && priv->config != NULL);
  974. /* Get exclusive access to read inputs and assess pending interrupts. */
  975. tca64_lock(priv);
  976. /* Read the input register for pin 0 through the number of supported pins.
  977. *
  978. * The Input Port Register reflects the incoming logic levels of the pins,
  979. * regardless of whether the pin is defined as an input or an output by
  980. * the Configuration Register. They act only on read operation.
  981. */
  982. ngpios = tca64_ngpios(priv);
  983. nregs = (ngpios + 7) >> 3;
  984. pinset = 0;
  985. regaddr = tca64_input_reg(priv, 0);
  986. ret = tca64_getreg(priv, regaddr, (FAR uint8_t *)&pinset, nregs);
  987. if (ret < 0)
  988. {
  989. gpioerr("ERROR: Failed to read input %u registers at %u: %d\n",
  990. nregs, regaddr, ret);
  991. tca64_unlock(priv);
  992. goto errout_with_restart;
  993. }
  994. /* Update the input status with the 32 bits read from the expander */
  995. tca64_int_update(priv, pinset, PINSET_ALL);
  996. /* Sample and clear the pending interrupts. */
  997. pinset = priv->intstat;
  998. priv->intstat = 0;
  999. tca64_unlock(priv);
  1000. /* Perform pin interrupt callbacks */
  1001. for (i = 0; i < CONFIG_TCA64XX_INT_NCALLBACKS; i++)
  1002. {
  1003. /* Is this entry valid (i.e., callback attached)? */
  1004. if (priv->cb[i].cbfunc != NULL)
  1005. {
  1006. /* Did any of the requested pin interrupts occur? */
  1007. ioe_pinset_t match = pinset & priv->cb[i].pinset;
  1008. if (match != 0)
  1009. {
  1010. /* Yes.. perform the callback */
  1011. (void)priv->cb[i].cbfunc(&priv->dev, match,
  1012. priv->cb[i].cbarg);
  1013. }
  1014. }
  1015. }
  1016. errout_with_restart:
  1017. #ifdef CONFIG_TCA64XX_INT_POLL
  1018. /* Check for pending interrupts */
  1019. tca64_register_update(priv);
  1020. /* Re-start the poll timer */
  1021. sched_lock();
  1022. ret = wd_start(priv->wdog, TCA64XX_POLLDELAY, (wdentry_t)tca64_poll_expiry,
  1023. 1, (wdparm_t)priv);
  1024. if (ret < 0)
  1025. {
  1026. gpioerr("ERROR: Failed to start poll timer\n");
  1027. }
  1028. #endif
  1029. /* Re-enable interrupts */
  1030. priv->config->enable(priv->config, true);
  1031. #ifdef CONFIG_TCA64XX_INT_POLL
  1032. sched_unlock();
  1033. #endif
  1034. }
  1035. #endif
  1036. /****************************************************************************
  1037. * Name: tca64_interrupt
  1038. *
  1039. * Description:
  1040. * Handle GPIO interrupt events (this function executes in the
  1041. * context of the interrupt).
  1042. *
  1043. ****************************************************************************/
  1044. #ifdef CONFIG_TCA64XX_INT_ENABLE
  1045. static void tca64_interrupt(FAR void *arg)
  1046. {
  1047. FAR struct tca64_dev_s *priv = (FAR struct tca64_dev_s *)arg;
  1048. DEBUGASSERT(priv != NULL && priv->config != NULL);
  1049. /* Defer interrupt processing to the worker thread. This is not only
  1050. * much kinder in the use of system resources but is probably necessary
  1051. * to access the I/O expander device.
  1052. *
  1053. * Notice that further GPIO interrupts are disabled until the work is
  1054. * actually performed. This is to prevent overrun of the worker thread.
  1055. * Interrupts are re-enabled in tca64_irqworker() when the work is
  1056. * completed.
  1057. */
  1058. if (work_available(&priv->work))
  1059. {
  1060. #ifdef CONFIG_TCA64XX_INT_POLL
  1061. /* Cancel the poll timer */
  1062. (void)wd_cancel(priv->wdog);
  1063. #endif
  1064. /* Disable interrupts */
  1065. priv->config->enable(priv->config, false);
  1066. /* Schedule interrupt related work on the high priority worker thread. */
  1067. work_queue(HPWORK, &priv->work, tca64_irqworker,
  1068. (FAR void *)priv, 0);
  1069. }
  1070. }
  1071. #endif
  1072. /****************************************************************************
  1073. * Name: tca64_poll_expiry
  1074. *
  1075. * Description:
  1076. * The poll timer has expired; check for missed interrupts
  1077. *
  1078. * Input Parameters:
  1079. * Standard wdog expiration arguments.
  1080. *
  1081. ****************************************************************************/
  1082. #if defined(CONFIG_TCA64XX_INT_ENABLE) && defined(CONFIG_TCA64XX_INT_POLL)
  1083. static void tca64_poll_expiry(int argc, wdparm_t arg1, ...)
  1084. {
  1085. FAR struct tca64_dev_s *priv;
  1086. DEBUGASSERT(argc == 1);
  1087. priv = (FAR struct tca64_dev_s *)arg1;
  1088. DEBUGASSERT(priv != NULL && priv->config != NULL);
  1089. /* Defer interrupt processing to the worker thread. This is not only
  1090. * much kinder in the use of system resources but is probably necessary
  1091. * to access the I/O expander device.
  1092. *
  1093. * Notice that further GPIO interrupts are disabled until the work is
  1094. * actually performed. This is to prevent overrun of the worker thread.
  1095. * Interrupts are re-enabled in tca64_irqworker() when the work is
  1096. * completed.
  1097. */
  1098. if (work_available(&priv->work))
  1099. {
  1100. /* Disable interrupts */
  1101. priv->config->enable(priv->config, false);
  1102. /* Schedule interrupt related work on the high priority worker thread. */
  1103. work_queue(HPWORK, &priv->work, tca64_irqworker,
  1104. (FAR void *)priv, 0);
  1105. }
  1106. }
  1107. #endif
  1108. /****************************************************************************
  1109. * Public Functions
  1110. ****************************************************************************/
  1111. /****************************************************************************
  1112. * Name: tca64_initialize
  1113. *
  1114. * Description:
  1115. * Instantiate and configure the TCA64xx device driver to use the provided
  1116. * I2C device instance.
  1117. *
  1118. * Input Parameters:
  1119. * i2c - An I2C driver instance
  1120. * minor - The device i2c address
  1121. * config - Persistent board configuration data
  1122. *
  1123. * Returned Value:
  1124. * an ioexpander_dev_s instance on success, NULL on failure.
  1125. *
  1126. ****************************************************************************/
  1127. FAR struct ioexpander_dev_s *tca64_initialize(FAR struct i2c_master_s *i2c,
  1128. FAR struct tca64_config_s *config)
  1129. {
  1130. FAR struct tca64_dev_s *priv;
  1131. int ret;
  1132. #ifdef CONFIG_TCA64XX_MULTIPLE
  1133. /* Allocate the device state structure */
  1134. priv = (FAR struct tca64_dev_s *)kmm_zalloc(sizeof(struct tca64_dev_s));
  1135. if (!priv)
  1136. {
  1137. gpioerr("ERROR: Failed to allocate driver instance\n");
  1138. return NULL;
  1139. }
  1140. #else
  1141. /* Use the one-and-only I/O Expander driver instance */
  1142. priv = &g_tca64;
  1143. #endif
  1144. /* Initialize the device state structure */
  1145. priv->dev.ops = &g_tca64_ops;
  1146. priv->i2c = i2c;
  1147. priv->config = config;
  1148. #ifdef CONFIG_TCA64XX_INT_ENABLE
  1149. /* Initial interrupt state: Edge triggered on both edges */
  1150. priv->trigger = PINSET_ALL; /* All edge triggered */
  1151. priv->level[0] = PINSET_ALL; /* All rising edge */
  1152. priv->level[1] = PINSET_ALL; /* All falling edge */
  1153. #ifdef CONFIG_TCA64XX_INT_POLL
  1154. /* Set up a timer to poll for missed interrupts */
  1155. priv->wdog = wd_create();
  1156. DEBUGASSERT(priv->wdog != NULL);
  1157. ret = wd_start(priv->wdog, TCA64XX_POLLDELAY, (wdentry_t)tca64_poll_expiry,
  1158. 1, (wdparm_t)priv);
  1159. if (ret < 0)
  1160. {
  1161. gpioerr("ERROR: Failed to start poll timer\n");
  1162. }
  1163. #endif
  1164. /* Attach the I/O expander interrupt handler and enable interrupts */
  1165. priv->config->attach(config, tca64_interrupt, priv);
  1166. priv->config->enable(config, true);
  1167. #endif
  1168. nxsem_init(&priv->exclsem, 0, 1);
  1169. return &priv->dev;
  1170. }
  1171. #endif /* CONFIG_IOEXPANDER_TCA64XX */