OpenWrt – Blame information for rev 4
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
4 | office | 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 | |||
11 | #include <linux/kernel.h> |
||
12 | #include <linux/init.h> |
||
13 | #include <linux/module.h> |
||
14 | #include <linux/types.h> |
||
15 | #include <linux/gpio.h> |
||
16 | #include <linux/slab.h> |
||
17 | #include <linux/platform_device.h> |
||
18 | |||
19 | #include <linux/platform_data/gpio-latch.h> |
||
20 | |||
21 | struct gpio_latch_chip { |
||
22 | struct gpio_chip gc; |
||
23 | |||
24 | struct mutex mutex; |
||
25 | struct mutex latch_mutex; |
||
26 | bool latch_enabled; |
||
27 | int le_gpio; |
||
28 | bool le_active_low; |
||
29 | int *gpios; |
||
30 | }; |
||
31 | |||
32 | static inline struct gpio_latch_chip *to_gpio_latch_chip(struct gpio_chip *gc) |
||
33 | { |
||
34 | return container_of(gc, struct gpio_latch_chip, gc); |
||
35 | } |
||
36 | |||
37 | static void gpio_latch_lock(struct gpio_latch_chip *glc, bool enable) |
||
38 | { |
||
39 | mutex_lock(&glc->mutex); |
||
40 | |||
41 | if (enable) |
||
42 | glc->latch_enabled = true; |
||
43 | |||
44 | if (glc->latch_enabled) |
||
45 | mutex_lock(&glc->latch_mutex); |
||
46 | } |
||
47 | |||
48 | static void gpio_latch_unlock(struct gpio_latch_chip *glc, bool disable) |
||
49 | { |
||
50 | if (glc->latch_enabled) |
||
51 | mutex_unlock(&glc->latch_mutex); |
||
52 | |||
53 | if (disable) |
||
54 | glc->latch_enabled = true; |
||
55 | |||
56 | mutex_unlock(&glc->mutex); |
||
57 | } |
||
58 | |||
59 | static int |
||
60 | gpio_latch_get(struct gpio_chip *gc, unsigned offset) |
||
61 | { |
||
62 | struct gpio_latch_chip *glc = to_gpio_latch_chip(gc); |
||
63 | int ret; |
||
64 | |||
65 | gpio_latch_lock(glc, false); |
||
66 | ret = gpio_get_value(glc->gpios[offset]); |
||
67 | gpio_latch_unlock(glc, false); |
||
68 | |||
69 | return ret; |
||
70 | } |
||
71 | |||
72 | static void |
||
73 | gpio_latch_set(struct gpio_chip *gc, unsigned offset, int value) |
||
74 | { |
||
75 | struct gpio_latch_chip *glc = to_gpio_latch_chip(gc); |
||
76 | bool enable_latch = false; |
||
77 | bool disable_latch = false; |
||
78 | int gpio; |
||
79 | |||
80 | gpio = glc->gpios[offset]; |
||
81 | |||
82 | if (gpio == glc->le_gpio) { |
||
83 | enable_latch = value ^ glc->le_active_low; |
||
84 | disable_latch = !enable_latch; |
||
85 | } |
||
86 | |||
87 | gpio_latch_lock(glc, enable_latch); |
||
88 | gpio_set_value(gpio, value); |
||
89 | gpio_latch_unlock(glc, disable_latch); |
||
90 | } |
||
91 | |||
92 | static int |
||
93 | gpio_latch_direction_input(struct gpio_chip *gc, unsigned offset) |
||
94 | { |
||
95 | struct gpio_latch_chip *glc = to_gpio_latch_chip(gc); |
||
96 | int ret; |
||
97 | |||
98 | gpio_latch_lock(glc, false); |
||
99 | ret = gpio_direction_input(glc->gpios[offset]); |
||
100 | gpio_latch_unlock(glc, false); |
||
101 | |||
102 | return ret; |
||
103 | } |
||
104 | |||
105 | static int |
||
106 | gpio_latch_direction_output(struct gpio_chip *gc, unsigned offset, int value) |
||
107 | { |
||
108 | struct gpio_latch_chip *glc = to_gpio_latch_chip(gc); |
||
109 | bool enable_latch = false; |
||
110 | bool disable_latch = false; |
||
111 | int gpio; |
||
112 | int ret; |
||
113 | |||
114 | gpio = glc->gpios[offset]; |
||
115 | |||
116 | if (gpio == glc->le_gpio) { |
||
117 | enable_latch = value ^ glc->le_active_low; |
||
118 | disable_latch = !enable_latch; |
||
119 | } |
||
120 | |||
121 | gpio_latch_lock(glc, enable_latch); |
||
122 | ret = gpio_direction_output(gpio, value); |
||
123 | gpio_latch_unlock(glc, disable_latch); |
||
124 | |||
125 | return ret; |
||
126 | } |
||
127 | |||
128 | static int gpio_latch_probe(struct platform_device *pdev) |
||
129 | { |
||
130 | struct gpio_latch_chip *glc; |
||
131 | struct gpio_latch_platform_data *pdata; |
||
132 | struct gpio_chip *gc; |
||
133 | int size; |
||
134 | int ret; |
||
135 | int i; |
||
136 | |||
137 | pdata = dev_get_platdata(&pdev->dev); |
||
138 | if (!pdata) |
||
139 | return -EINVAL; |
||
140 | |||
141 | if (pdata->le_gpio_index >= pdata->num_gpios || |
||
142 | !pdata->num_gpios || |
||
143 | !pdata->gpios) |
||
144 | return -EINVAL; |
||
145 | |||
146 | for (i = 0; i < pdata->num_gpios; i++) { |
||
147 | int gpio = pdata->gpios[i]; |
||
148 | |||
149 | ret = devm_gpio_request(&pdev->dev, gpio, |
||
150 | GPIO_LATCH_DRIVER_NAME); |
||
151 | if (ret) |
||
152 | return ret; |
||
153 | } |
||
154 | |||
155 | glc = devm_kzalloc(&pdev->dev, sizeof(*glc), GFP_KERNEL); |
||
156 | if (!glc) |
||
157 | return -ENOMEM; |
||
158 | |||
159 | mutex_init(&glc->mutex); |
||
160 | mutex_init(&glc->latch_mutex); |
||
161 | |||
162 | size = pdata->num_gpios * sizeof(glc->gpios[0]); |
||
163 | glc->gpios = devm_kzalloc(&pdev->dev, size , GFP_KERNEL); |
||
164 | if (!glc->gpios) |
||
165 | return -ENOMEM; |
||
166 | |||
167 | memcpy(glc->gpios, pdata->gpios, size); |
||
168 | |||
169 | glc->le_gpio = glc->gpios[pdata->le_gpio_index]; |
||
170 | glc->le_active_low = pdata->le_active_low; |
||
171 | |||
172 | gc = &glc->gc; |
||
173 | |||
174 | gc->label = GPIO_LATCH_DRIVER_NAME; |
||
175 | gc->base = pdata->base; |
||
176 | gc->can_sleep = true; |
||
177 | gc->ngpio = pdata->num_gpios; |
||
178 | gc->get = gpio_latch_get; |
||
179 | gc->set = gpio_latch_set; |
||
180 | gc->direction_input = gpio_latch_direction_input, |
||
181 | gc->direction_output = gpio_latch_direction_output; |
||
182 | |||
183 | platform_set_drvdata(pdev, glc); |
||
184 | |||
185 | ret = gpiochip_add(&glc->gc); |
||
186 | if (ret) |
||
187 | return ret; |
||
188 | |||
189 | return 0; |
||
190 | } |
||
191 | |||
192 | static int gpio_latch_remove(struct platform_device *pdev) |
||
193 | { |
||
194 | struct gpio_latch_chip *glc = platform_get_drvdata(pdev); |
||
195 | |||
196 | gpiochip_remove(&glc->gc); |
||
197 | return 0; |
||
198 | } |
||
199 | |||
200 | |||
201 | static struct platform_driver gpio_latch_driver = { |
||
202 | .probe = gpio_latch_probe, |
||
203 | .remove = gpio_latch_remove, |
||
204 | .driver = { |
||
205 | .name = GPIO_LATCH_DRIVER_NAME, |
||
206 | .owner = THIS_MODULE, |
||
207 | }, |
||
208 | }; |
||
209 | |||
210 | static int __init gpio_latch_init(void) |
||
211 | { |
||
212 | return platform_driver_register(&gpio_latch_driver); |
||
213 | } |
||
214 | |||
215 | postcore_initcall(gpio_latch_init); |
||
216 | |||
217 | MODULE_DESCRIPTION("GPIO latch driver"); |
||
218 | MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>"); |
||
219 | MODULE_LICENSE("GPL v2"); |
||
220 | MODULE_ALIAS("platform:" GPIO_LATCH_DRIVER_NAME); |