0008-gpio-add-GPIO-latch-driver.patch 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290
  1. From dd93d7e5b6530f1574860776fe6f960c4fd2661d Mon Sep 17 00:00:00 2001
  2. From: Phil Sutter <phil@nwl.cc>
  3. Date: Tue, 13 May 2014 00:21:54 +0200
  4. Subject: [PATCH] gpio: add GPIO latch driver
  5. ---
  6. drivers/gpio/Kconfig | 7 +
  7. drivers/gpio/Makefile | 1 +
  8. drivers/gpio/gpio-latch.c | 219 +++++++++++++++++++++++++++++++
  9. include/linux/platform_data/gpio-latch.h | 14 ++
  10. 4 files changed, 241 insertions(+)
  11. create mode 100644 drivers/gpio/gpio-latch.c
  12. create mode 100644 include/linux/platform_data/gpio-latch.h
  13. diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
  14. index 903f24d..905730b 100644
  15. --- a/drivers/gpio/Kconfig
  16. +++ b/drivers/gpio/Kconfig
  17. @@ -834,4 +834,11 @@ config GPIO_VIPERBOARD
  18. River Tech's viperboard.h for detailed meaning
  19. of the module parameters.
  20. +comment "Other GPIO expanders"
  21. +
  22. +config GPIO_LATCH
  23. + tristate "GPIO latch driver"
  24. + help
  25. + Say yes here to enable a GPIO latch driver.
  26. +
  27. endif
  28. diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
  29. index 5d50179..7d03524 100644
  30. --- a/drivers/gpio/Makefile
  31. +++ b/drivers/gpio/Makefile
  32. @@ -36,6 +36,7 @@ obj-$(CONFIG_GPIO_KEMPLD) += gpio-kempld.o
  33. obj-$(CONFIG_ARCH_KS8695) += gpio-ks8695.o
  34. obj-$(CONFIG_GPIO_INTEL_MID) += gpio-intel-mid.o
  35. obj-$(CONFIG_GPIO_LP3943) += gpio-lp3943.o
  36. +obj-$(CONFIG_GPIO_LATCH) += gpio-latch.o
  37. obj-$(CONFIG_ARCH_LPC32XX) += gpio-lpc32xx.o
  38. obj-$(CONFIG_GPIO_LYNXPOINT) += gpio-lynxpoint.o
  39. obj-$(CONFIG_GPIO_MAX730X) += gpio-max730x.o
  40. diff --git a/drivers/gpio/gpio-latch.c b/drivers/gpio/gpio-latch.c
  41. new file mode 100644
  42. index 0000000..1efa1a1
  43. --- /dev/null
  44. +++ b/drivers/gpio/gpio-latch.c
  45. @@ -0,0 +1,219 @@
  46. +/*
  47. + * GPIO latch driver
  48. + *
  49. + * Copyright (C) 2014 Gabor Juhos <juhosg@openwrt.org>
  50. + *
  51. + * This program is free software; you can redistribute it and/or modify it
  52. + * under the terms of the GNU General Public License version 2 as published
  53. + * by the Free Software Foundation.
  54. + */
  55. +
  56. +#include <linux/kernel.h>
  57. +#include <linux/init.h>
  58. +#include <linux/module.h>
  59. +#include <linux/types.h>
  60. +#include <linux/gpio.h>
  61. +#include <linux/slab.h>
  62. +#include <linux/platform_device.h>
  63. +
  64. +#include <linux/platform_data/gpio-latch.h>
  65. +
  66. +struct gpio_latch_chip {
  67. + struct gpio_chip gc;
  68. +
  69. + struct mutex mutex;
  70. + struct mutex latch_mutex;
  71. + bool latch_enabled;
  72. + int le_gpio;
  73. + bool le_active_low;
  74. + int *gpios;
  75. +};
  76. +
  77. +static inline struct gpio_latch_chip *to_gpio_latch_chip(struct gpio_chip *gc)
  78. +{
  79. + return container_of(gc, struct gpio_latch_chip, gc);
  80. +}
  81. +
  82. +static void gpio_latch_lock(struct gpio_latch_chip *glc, bool enable)
  83. +{
  84. + mutex_lock(&glc->mutex);
  85. +
  86. + if (enable)
  87. + glc->latch_enabled = true;
  88. +
  89. + if (glc->latch_enabled)
  90. + mutex_lock(&glc->latch_mutex);
  91. +}
  92. +
  93. +static void gpio_latch_unlock(struct gpio_latch_chip *glc, bool disable)
  94. +{
  95. + if (glc->latch_enabled)
  96. + mutex_unlock(&glc->latch_mutex);
  97. +
  98. + if (disable)
  99. + glc->latch_enabled = true;
  100. +
  101. + mutex_unlock(&glc->mutex);
  102. +}
  103. +
  104. +static int
  105. +gpio_latch_get(struct gpio_chip *gc, unsigned offset)
  106. +{
  107. + struct gpio_latch_chip *glc = to_gpio_latch_chip(gc);
  108. + int ret;
  109. +
  110. + gpio_latch_lock(glc, false);
  111. + ret = gpio_get_value(glc->gpios[offset]);
  112. + gpio_latch_unlock(glc, false);
  113. +
  114. + return ret;
  115. +}
  116. +
  117. +static void
  118. +gpio_latch_set(struct gpio_chip *gc, unsigned offset, int value)
  119. +{
  120. + struct gpio_latch_chip *glc = to_gpio_latch_chip(gc);
  121. + bool enable_latch = false;
  122. + bool disable_latch = false;
  123. + int gpio;
  124. +
  125. + gpio = glc->gpios[offset];
  126. +
  127. + if (gpio == glc->le_gpio) {
  128. + enable_latch = value ^ glc->le_active_low;
  129. + disable_latch = !enable_latch;
  130. + }
  131. +
  132. + gpio_latch_lock(glc, enable_latch);
  133. + gpio_set_value(gpio, value);
  134. + gpio_latch_unlock(glc, disable_latch);
  135. +}
  136. +
  137. +static int
  138. +gpio_latch_direction_input(struct gpio_chip *gc, unsigned offset)
  139. +{
  140. + struct gpio_latch_chip *glc = to_gpio_latch_chip(gc);
  141. + int ret;
  142. +
  143. + gpio_latch_lock(glc, false);
  144. + ret = gpio_direction_input(glc->gpios[offset]);
  145. + gpio_latch_unlock(glc, false);
  146. +
  147. + return ret;
  148. +}
  149. +
  150. +static int
  151. +gpio_latch_direction_output(struct gpio_chip *gc, unsigned offset, int value)
  152. +{
  153. + struct gpio_latch_chip *glc = to_gpio_latch_chip(gc);
  154. + bool enable_latch = false;
  155. + bool disable_latch = false;
  156. + int gpio;
  157. + int ret;
  158. +
  159. + gpio = glc->gpios[offset];
  160. +
  161. + if (gpio == glc->le_gpio) {
  162. + enable_latch = value ^ glc->le_active_low;
  163. + disable_latch = !enable_latch;
  164. + }
  165. +
  166. + gpio_latch_lock(glc, enable_latch);
  167. + ret = gpio_direction_output(gpio, value);
  168. + gpio_latch_unlock(glc, disable_latch);
  169. +
  170. + return ret;
  171. +}
  172. +
  173. +static int gpio_latch_probe(struct platform_device *pdev)
  174. +{
  175. + struct gpio_latch_chip *glc;
  176. + struct gpio_latch_platform_data *pdata;
  177. + struct gpio_chip *gc;
  178. + int size;
  179. + int ret;
  180. + int i;
  181. +
  182. + pdata = dev_get_platdata(&pdev->dev);
  183. + if (!pdata)
  184. + return -EINVAL;
  185. +
  186. + if (pdata->le_gpio_index >= pdata->num_gpios ||
  187. + !pdata->num_gpios ||
  188. + !pdata->gpios)
  189. + return -EINVAL;
  190. +
  191. + for (i = 0; i < pdata->num_gpios; i++) {
  192. + int gpio = pdata->gpios[i];
  193. +
  194. + ret = devm_gpio_request(&pdev->dev, gpio,
  195. + GPIO_LATCH_DRIVER_NAME);
  196. + if (ret)
  197. + return ret;
  198. + }
  199. +
  200. + glc = devm_kzalloc(&pdev->dev, sizeof(*glc), GFP_KERNEL);
  201. + if (!glc)
  202. + return -ENOMEM;
  203. +
  204. + mutex_init(&glc->mutex);
  205. + mutex_init(&glc->latch_mutex);
  206. +
  207. + size = pdata->num_gpios * sizeof(glc->gpios[0]);
  208. + glc->gpios = devm_kzalloc(&pdev->dev, size , GFP_KERNEL);
  209. + if (!glc->gpios)
  210. + return -ENOMEM;
  211. +
  212. + memcpy(glc->gpios, pdata->gpios, size);
  213. +
  214. + glc->le_gpio = glc->gpios[pdata->le_gpio_index];
  215. + glc->le_active_low = pdata->le_active_low;
  216. +
  217. + gc = &glc->gc;
  218. +
  219. + gc->label = GPIO_LATCH_DRIVER_NAME;
  220. + gc->base = pdata->base;
  221. + gc->can_sleep = true;
  222. + gc->ngpio = pdata->num_gpios;
  223. + gc->get = gpio_latch_get;
  224. + gc->set = gpio_latch_set;
  225. + gc->direction_input = gpio_latch_direction_input,
  226. + gc->direction_output = gpio_latch_direction_output;
  227. +
  228. + platform_set_drvdata(pdev, glc);
  229. +
  230. + ret = gpiochip_add(&glc->gc);
  231. + if (ret)
  232. + return ret;
  233. +
  234. + return 0;
  235. +}
  236. +
  237. +static int gpio_latch_remove(struct platform_device *pdev)
  238. +{
  239. + struct gpio_latch_chip *glc = platform_get_drvdata(pdev);
  240. +
  241. + return gpiochip_remove(&glc->gc);;
  242. +}
  243. +
  244. +
  245. +static struct platform_driver gpio_latch_driver = {
  246. + .probe = gpio_latch_probe,
  247. + .remove = gpio_latch_remove,
  248. + .driver = {
  249. + .name = GPIO_LATCH_DRIVER_NAME,
  250. + .owner = THIS_MODULE,
  251. + },
  252. +};
  253. +
  254. +static int __init gpio_latch_init(void)
  255. +{
  256. + return platform_driver_register(&gpio_latch_driver);
  257. +}
  258. +
  259. +postcore_initcall(gpio_latch_init);
  260. +
  261. +MODULE_DESCRIPTION("GPIO latch driver");
  262. +MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>");
  263. +MODULE_LICENSE("GPL v2");
  264. +MODULE_ALIAS("platform:" GPIO_LATCH_DRIVER_NAME);
  265. diff --git a/include/linux/platform_data/gpio-latch.h b/include/linux/platform_data/gpio-latch.h
  266. new file mode 100644
  267. index 0000000..0450e67
  268. --- /dev/null
  269. +++ b/include/linux/platform_data/gpio-latch.h
  270. @@ -0,0 +1,14 @@
  271. +#ifndef _GPIO_LATCH_H_
  272. +#define _GPIO_LATCH_H_
  273. +
  274. +#define GPIO_LATCH_DRIVER_NAME "gpio-latch"
  275. +
  276. +struct gpio_latch_platform_data {
  277. + int base;
  278. + int num_gpios;
  279. + int *gpios;
  280. + int le_gpio_index;
  281. + bool le_active_low;
  282. +};
  283. +
  284. +#endif /* _GPIO_LATCH_H_ */
  285. --
  286. 1.8.5.3