1
0

gpio-latch.c 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. /*
  2. * GPIO latch driver
  3. *
  4. * Copyright (C) 2014 Gabor Juhos <juhosg@openwrt.org>
  5. *
  6. * This program is free software; you can redistribute it and/or modify it
  7. * under the terms of the GNU General Public License version 2 as published
  8. * by the Free Software Foundation.
  9. */
  10. #include <linux/kernel.h>
  11. #include <linux/init.h>
  12. #include <linux/module.h>
  13. #include <linux/types.h>
  14. #include <linux/gpio.h>
  15. #include <linux/slab.h>
  16. #include <linux/platform_device.h>
  17. #include <linux/platform_data/gpio-latch.h>
  18. struct gpio_latch_chip {
  19. struct gpio_chip gc;
  20. struct mutex mutex;
  21. struct mutex latch_mutex;
  22. bool latch_enabled;
  23. int le_gpio;
  24. bool le_active_low;
  25. int *gpios;
  26. };
  27. static inline struct gpio_latch_chip *to_gpio_latch_chip(struct gpio_chip *gc)
  28. {
  29. return container_of(gc, struct gpio_latch_chip, gc);
  30. }
  31. static void gpio_latch_lock(struct gpio_latch_chip *glc, bool enable)
  32. {
  33. mutex_lock(&glc->mutex);
  34. if (enable)
  35. glc->latch_enabled = true;
  36. if (glc->latch_enabled)
  37. mutex_lock(&glc->latch_mutex);
  38. }
  39. static void gpio_latch_unlock(struct gpio_latch_chip *glc, bool disable)
  40. {
  41. if (glc->latch_enabled)
  42. mutex_unlock(&glc->latch_mutex);
  43. if (disable)
  44. glc->latch_enabled = true;
  45. mutex_unlock(&glc->mutex);
  46. }
  47. static int
  48. gpio_latch_get(struct gpio_chip *gc, unsigned offset)
  49. {
  50. struct gpio_latch_chip *glc = to_gpio_latch_chip(gc);
  51. int ret;
  52. gpio_latch_lock(glc, false);
  53. ret = gpio_get_value(glc->gpios[offset]);
  54. gpio_latch_unlock(glc, false);
  55. return ret;
  56. }
  57. static void
  58. gpio_latch_set(struct gpio_chip *gc, unsigned offset, int value)
  59. {
  60. struct gpio_latch_chip *glc = to_gpio_latch_chip(gc);
  61. bool enable_latch = false;
  62. bool disable_latch = false;
  63. int gpio;
  64. gpio = glc->gpios[offset];
  65. if (gpio == glc->le_gpio) {
  66. enable_latch = value ^ glc->le_active_low;
  67. disable_latch = !enable_latch;
  68. }
  69. gpio_latch_lock(glc, enable_latch);
  70. gpio_set_value(gpio, value);
  71. gpio_latch_unlock(glc, disable_latch);
  72. }
  73. static int
  74. gpio_latch_direction_input(struct gpio_chip *gc, unsigned offset)
  75. {
  76. struct gpio_latch_chip *glc = to_gpio_latch_chip(gc);
  77. int ret;
  78. gpio_latch_lock(glc, false);
  79. ret = gpio_direction_input(glc->gpios[offset]);
  80. gpio_latch_unlock(glc, false);
  81. return ret;
  82. }
  83. static int
  84. gpio_latch_direction_output(struct gpio_chip *gc, unsigned offset, int value)
  85. {
  86. struct gpio_latch_chip *glc = to_gpio_latch_chip(gc);
  87. bool enable_latch = false;
  88. bool disable_latch = false;
  89. int gpio;
  90. int ret;
  91. gpio = glc->gpios[offset];
  92. if (gpio == glc->le_gpio) {
  93. enable_latch = value ^ glc->le_active_low;
  94. disable_latch = !enable_latch;
  95. }
  96. gpio_latch_lock(glc, enable_latch);
  97. ret = gpio_direction_output(gpio, value);
  98. gpio_latch_unlock(glc, disable_latch);
  99. return ret;
  100. }
  101. static int gpio_latch_probe(struct platform_device *pdev)
  102. {
  103. struct gpio_latch_chip *glc;
  104. struct gpio_latch_platform_data *pdata;
  105. struct gpio_chip *gc;
  106. int size;
  107. int ret;
  108. int i;
  109. pdata = dev_get_platdata(&pdev->dev);
  110. if (!pdata)
  111. return -EINVAL;
  112. if (pdata->le_gpio_index >= pdata->num_gpios ||
  113. !pdata->num_gpios ||
  114. !pdata->gpios)
  115. return -EINVAL;
  116. for (i = 0; i < pdata->num_gpios; i++) {
  117. int gpio = pdata->gpios[i];
  118. ret = devm_gpio_request(&pdev->dev, gpio,
  119. GPIO_LATCH_DRIVER_NAME);
  120. if (ret)
  121. return ret;
  122. }
  123. glc = devm_kzalloc(&pdev->dev, sizeof(*glc), GFP_KERNEL);
  124. if (!glc)
  125. return -ENOMEM;
  126. mutex_init(&glc->mutex);
  127. mutex_init(&glc->latch_mutex);
  128. size = pdata->num_gpios * sizeof(glc->gpios[0]);
  129. glc->gpios = devm_kzalloc(&pdev->dev, size , GFP_KERNEL);
  130. if (!glc->gpios)
  131. return -ENOMEM;
  132. memcpy(glc->gpios, pdata->gpios, size);
  133. glc->le_gpio = glc->gpios[pdata->le_gpio_index];
  134. glc->le_active_low = pdata->le_active_low;
  135. gc = &glc->gc;
  136. gc->label = GPIO_LATCH_DRIVER_NAME;
  137. gc->base = pdata->base;
  138. gc->can_sleep = true;
  139. gc->ngpio = pdata->num_gpios;
  140. gc->get = gpio_latch_get;
  141. gc->set = gpio_latch_set;
  142. gc->direction_input = gpio_latch_direction_input,
  143. gc->direction_output = gpio_latch_direction_output;
  144. platform_set_drvdata(pdev, glc);
  145. ret = gpiochip_add(&glc->gc);
  146. if (ret)
  147. return ret;
  148. return 0;
  149. }
  150. static int gpio_latch_remove(struct platform_device *pdev)
  151. {
  152. struct gpio_latch_chip *glc = platform_get_drvdata(pdev);
  153. gpiochip_remove(&glc->gc);
  154. return 0;
  155. }
  156. static struct platform_driver gpio_latch_driver = {
  157. .probe = gpio_latch_probe,
  158. .remove = gpio_latch_remove,
  159. .driver = {
  160. .name = GPIO_LATCH_DRIVER_NAME,
  161. .owner = THIS_MODULE,
  162. },
  163. };
  164. static int __init gpio_latch_init(void)
  165. {
  166. return platform_driver_register(&gpio_latch_driver);
  167. }
  168. postcore_initcall(gpio_latch_init);
  169. MODULE_DESCRIPTION("GPIO latch driver");
  170. MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>");
  171. MODULE_LICENSE("GPL v2");
  172. MODULE_ALIAS("platform:" GPIO_LATCH_DRIVER_NAME);