Blame view
drivers/gpio/gpiolib-sysfs.c
19.4 KB
dae5f0afc gpio: Use SPDX he... |
1 |
// SPDX-License-Identifier: GPL-2.0 |
0eb4c6c26 gpio: move sysfs ... |
2 3 4 5 6 7 8 9 |
#include <linux/idr.h> #include <linux/mutex.h> #include <linux/device.h> #include <linux/sysfs.h> #include <linux/gpio/consumer.h> #include <linux/gpio/driver.h> #include <linux/interrupt.h> #include <linux/kdev_t.h> |
c43960fbc gpio: sysfs: add ... |
10 |
#include <linux/slab.h> |
1efba35af gpio: sysfs: avoi... |
11 |
#include <linux/ctype.h> |
0eb4c6c26 gpio: move sysfs ... |
12 13 |
#include "gpiolib.h" |
ef087d8e9 gpiolib: move gpi... |
14 |
#include "gpiolib-sysfs.h" |
0eb4c6c26 gpio: move sysfs ... |
15 |
|
cef1717b7 gpio: sysfs: move... |
16 17 18 19 |
#define GPIO_IRQF_TRIGGER_FALLING BIT(0) #define GPIO_IRQF_TRIGGER_RISING BIT(1) #define GPIO_IRQF_TRIGGER_BOTH (GPIO_IRQF_TRIGGER_FALLING | \ GPIO_IRQF_TRIGGER_RISING) |
c43960fbc gpio: sysfs: add ... |
20 21 |
struct gpiod_data { struct gpio_desc *desc; |
6ffcb7971 gpio: sysfs: use ... |
22 23 |
struct mutex mutex; |
a08f5c21f gpio: sysfs: clea... |
24 |
struct kernfs_node *value_kn; |
2ec74a959 gpio: sysfs: spli... |
25 |
int irq; |
cef1717b7 gpio: sysfs: move... |
26 |
unsigned char irq_flags; |
427fdeef5 gpio: sysfs: remo... |
27 28 |
bool direction_can_change; |
c43960fbc gpio: sysfs: add ... |
29 |
}; |
6ffcb7971 gpio: sysfs: use ... |
30 31 32 |
/* * Lock to serialise gpiod export and unexport, and prevent re-export of * gpiod whose chip is being unregistered. |
0eb4c6c26 gpio: move sysfs ... |
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
*/ static DEFINE_MUTEX(sysfs_lock); /* * /sys/class/gpio/gpioN... only for GPIOs that are exported * /direction * * MAY BE OMITTED if kernel won't allow direction changes * * is read/write as "in" or "out" * * may also be written as "high" or "low", initializing * output value as specified ("out" implies "low") * /value * * always readable, subject to hardware behavior * * may be writable, as zero/nonzero * /edge * * configures behavior of poll(2) on /value * * available only if pin can generate IRQs on input * * is read/write as "none", "falling", "rising", or "both" * /active_low * * configures polarity of /value * * is read/write as zero/nonzero * * also affects existing and subsequent "falling" and "rising" * /edge configuration */ |
6beac9d1a gpio: sysfs: use ... |
56 |
static ssize_t direction_show(struct device *dev, |
0eb4c6c26 gpio: move sysfs ... |
57 58 |
struct device_attribute *attr, char *buf) { |
c43960fbc gpio: sysfs: add ... |
59 60 |
struct gpiod_data *data = dev_get_drvdata(dev); struct gpio_desc *desc = data->desc; |
0eb4c6c26 gpio: move sysfs ... |
61 |
ssize_t status; |
6ffcb7971 gpio: sysfs: use ... |
62 |
mutex_lock(&data->mutex); |
0eb4c6c26 gpio: move sysfs ... |
63 |
|
f0b7866a0 gpio: sysfs: remo... |
64 65 66 |
gpiod_get_direction(desc); status = sprintf(buf, "%s ", |
0eb4c6c26 gpio: move sysfs ... |
67 68 |
test_bit(FLAG_IS_OUT, &desc->flags) ? "out" : "in"); |
0eb4c6c26 gpio: move sysfs ... |
69 |
|
6ffcb7971 gpio: sysfs: use ... |
70 |
mutex_unlock(&data->mutex); |
0eb4c6c26 gpio: move sysfs ... |
71 72 |
return status; } |
6beac9d1a gpio: sysfs: use ... |
73 |
static ssize_t direction_store(struct device *dev, |
0eb4c6c26 gpio: move sysfs ... |
74 75 |
struct device_attribute *attr, const char *buf, size_t size) { |
c43960fbc gpio: sysfs: add ... |
76 77 |
struct gpiod_data *data = dev_get_drvdata(dev); struct gpio_desc *desc = data->desc; |
0eb4c6c26 gpio: move sysfs ... |
78 |
ssize_t status; |
6ffcb7971 gpio: sysfs: use ... |
79 |
mutex_lock(&data->mutex); |
0eb4c6c26 gpio: move sysfs ... |
80 |
|
f0b7866a0 gpio: sysfs: remo... |
81 |
if (sysfs_streq(buf, "high")) |
0eb4c6c26 gpio: move sysfs ... |
82 83 84 85 86 87 88 |
status = gpiod_direction_output_raw(desc, 1); else if (sysfs_streq(buf, "out") || sysfs_streq(buf, "low")) status = gpiod_direction_output_raw(desc, 0); else if (sysfs_streq(buf, "in")) status = gpiod_direction_input(desc); else status = -EINVAL; |
6ffcb7971 gpio: sysfs: use ... |
89 |
mutex_unlock(&data->mutex); |
0eb4c6c26 gpio: move sysfs ... |
90 91 |
return status ? : size; } |
6beac9d1a gpio: sysfs: use ... |
92 |
static DEVICE_ATTR_RW(direction); |
0eb4c6c26 gpio: move sysfs ... |
93 |
|
6beac9d1a gpio: sysfs: use ... |
94 |
static ssize_t value_show(struct device *dev, |
0eb4c6c26 gpio: move sysfs ... |
95 96 |
struct device_attribute *attr, char *buf) { |
c43960fbc gpio: sysfs: add ... |
97 98 |
struct gpiod_data *data = dev_get_drvdata(dev); struct gpio_desc *desc = data->desc; |
0eb4c6c26 gpio: move sysfs ... |
99 |
ssize_t status; |
6ffcb7971 gpio: sysfs: use ... |
100 |
mutex_lock(&data->mutex); |
0eb4c6c26 gpio: move sysfs ... |
101 |
|
9295c0125 gpio: sysfs: corr... |
102 103 104 |
status = gpiod_get_value_cansleep(desc); if (status < 0) goto err; |
0eb4c6c26 gpio: move sysfs ... |
105 |
|
7a94b88cb gpio: sysfs: don'... |
106 107 108 109 |
buf[0] = '0' + status; buf[1] = ' '; status = 2; |
9295c0125 gpio: sysfs: corr... |
110 |
err: |
6ffcb7971 gpio: sysfs: use ... |
111 |
mutex_unlock(&data->mutex); |
0eb4c6c26 gpio: move sysfs ... |
112 113 |
return status; } |
6beac9d1a gpio: sysfs: use ... |
114 |
static ssize_t value_store(struct device *dev, |
0eb4c6c26 gpio: move sysfs ... |
115 116 |
struct device_attribute *attr, const char *buf, size_t size) { |
c43960fbc gpio: sysfs: add ... |
117 118 |
struct gpiod_data *data = dev_get_drvdata(dev); struct gpio_desc *desc = data->desc; |
1efba35af gpio: sysfs: avoi... |
119 |
ssize_t status = 0; |
0eb4c6c26 gpio: move sysfs ... |
120 |
|
6ffcb7971 gpio: sysfs: use ... |
121 |
mutex_lock(&data->mutex); |
0eb4c6c26 gpio: move sysfs ... |
122 |
|
f0b7866a0 gpio: sysfs: remo... |
123 |
if (!test_bit(FLAG_IS_OUT, &desc->flags)) { |
0eb4c6c26 gpio: move sysfs ... |
124 |
status = -EPERM; |
f0b7866a0 gpio: sysfs: remo... |
125 |
} else { |
0eb4c6c26 gpio: move sysfs ... |
126 |
long value; |
1efba35af gpio: sysfs: avoi... |
127 128 129 130 131 132 |
if (size <= 2 && isdigit(buf[0]) && (size == 1 || buf[1] == ' ')) value = buf[0] - '0'; else status = kstrtol(buf, 0, &value); |
0eb4c6c26 gpio: move sysfs ... |
133 134 135 136 137 |
if (status == 0) { gpiod_set_value_cansleep(desc, value); status = size; } } |
6ffcb7971 gpio: sysfs: use ... |
138 |
mutex_unlock(&data->mutex); |
0eb4c6c26 gpio: move sysfs ... |
139 140 |
return status; } |
7fda9100b gpio: sysfs: chan... |
141 |
static DEVICE_ATTR_PREALLOC(value, S_IWUSR | S_IRUGO, value_show, value_store); |
0eb4c6c26 gpio: move sysfs ... |
142 143 144 |
static irqreturn_t gpio_sysfs_irq(int irq, void *priv) { |
a08f5c21f gpio: sysfs: clea... |
145 146 147 |
struct gpiod_data *data = priv; sysfs_notify_dirent(data->value_kn); |
0eb4c6c26 gpio: move sysfs ... |
148 |
|
0eb4c6c26 gpio: move sysfs ... |
149 150 |
return IRQ_HANDLED; } |
6ffcb7971 gpio: sysfs: use ... |
151 |
/* Caller holds gpiod-data mutex. */ |
cef1717b7 gpio: sysfs: move... |
152 |
static int gpio_sysfs_request_irq(struct device *dev, unsigned char flags) |
0eb4c6c26 gpio: move sysfs ... |
153 |
{ |
0f6285080 gpio: sysfs: remo... |
154 155 |
struct gpiod_data *data = dev_get_drvdata(dev); struct gpio_desc *desc = data->desc; |
0eb4c6c26 gpio: move sysfs ... |
156 |
unsigned long irq_flags; |
2ec74a959 gpio: sysfs: spli... |
157 |
int ret; |
0eb4c6c26 gpio: move sysfs ... |
158 |
|
2ec74a959 gpio: sysfs: spli... |
159 160 |
data->irq = gpiod_to_irq(desc); if (data->irq < 0) |
0eb4c6c26 gpio: move sysfs ... |
161 |
return -EIO; |
2ec74a959 gpio: sysfs: spli... |
162 163 164 |
data->value_kn = sysfs_get_dirent(dev->kobj.sd, "value"); if (!data->value_kn) return -ENODEV; |
0eb4c6c26 gpio: move sysfs ... |
165 166 |
irq_flags = IRQF_SHARED; |
cef1717b7 gpio: sysfs: move... |
167 |
if (flags & GPIO_IRQF_TRIGGER_FALLING) |
0eb4c6c26 gpio: move sysfs ... |
168 169 |
irq_flags |= test_bit(FLAG_ACTIVE_LOW, &desc->flags) ? IRQF_TRIGGER_RISING : IRQF_TRIGGER_FALLING; |
cef1717b7 gpio: sysfs: move... |
170 |
if (flags & GPIO_IRQF_TRIGGER_RISING) |
0eb4c6c26 gpio: move sysfs ... |
171 172 |
irq_flags |= test_bit(FLAG_ACTIVE_LOW, &desc->flags) ? IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING; |
52176d0d3 gpio: sysfs: fix ... |
173 174 175 176 177 178 179 180 |
/* * FIXME: This should be done in the irq_request_resources callback * when the irq is requested, but a few drivers currently fail * to do so. * * Remove this redundant call (along with the corresponding * unlock) when those drivers have been fixed. */ |
fdeb8e154 gpio: reflect bas... |
181 |
ret = gpiochip_lock_as_irq(desc->gdev->chip, gpio_chip_hwgpio(desc)); |
0eb4c6c26 gpio: move sysfs ... |
182 |
if (ret < 0) |
2ec74a959 gpio: sysfs: spli... |
183 |
goto err_put_kn; |
0eb4c6c26 gpio: move sysfs ... |
184 |
|
2ec74a959 gpio: sysfs: spli... |
185 |
ret = request_any_context_irq(data->irq, gpio_sysfs_irq, irq_flags, |
a08f5c21f gpio: sysfs: clea... |
186 |
"gpiolib", data); |
52176d0d3 gpio: sysfs: fix ... |
187 188 |
if (ret < 0) goto err_unlock; |
0eb4c6c26 gpio: move sysfs ... |
189 |
|
cef1717b7 gpio: sysfs: move... |
190 |
data->irq_flags = flags; |
2ec74a959 gpio: sysfs: spli... |
191 |
|
0eb4c6c26 gpio: move sysfs ... |
192 |
return 0; |
52176d0d3 gpio: sysfs: fix ... |
193 |
err_unlock: |
fdeb8e154 gpio: reflect bas... |
194 |
gpiochip_unlock_as_irq(desc->gdev->chip, gpio_chip_hwgpio(desc)); |
2ec74a959 gpio: sysfs: spli... |
195 196 |
err_put_kn: sysfs_put(data->value_kn); |
0eb4c6c26 gpio: move sysfs ... |
197 198 |
return ret; } |
6ffcb7971 gpio: sysfs: use ... |
199 200 201 202 |
/* * Caller holds gpiod-data mutex (unless called after class-device * deregistration). */ |
2ec74a959 gpio: sysfs: spli... |
203 204 205 206 |
static void gpio_sysfs_free_irq(struct device *dev) { struct gpiod_data *data = dev_get_drvdata(dev); struct gpio_desc *desc = data->desc; |
cef1717b7 gpio: sysfs: move... |
207 |
data->irq_flags = 0; |
2ec74a959 gpio: sysfs: spli... |
208 |
free_irq(data->irq, data); |
fdeb8e154 gpio: reflect bas... |
209 |
gpiochip_unlock_as_irq(desc->gdev->chip, gpio_chip_hwgpio(desc)); |
2ec74a959 gpio: sysfs: spli... |
210 211 |
sysfs_put(data->value_kn); } |
0eb4c6c26 gpio: move sysfs ... |
212 213 |
static const struct { const char *name; |
cef1717b7 gpio: sysfs: move... |
214 |
unsigned char flags; |
0eb4c6c26 gpio: move sysfs ... |
215 216 |
} trigger_types[] = { { "none", 0 }, |
cef1717b7 gpio: sysfs: move... |
217 218 219 |
{ "falling", GPIO_IRQF_TRIGGER_FALLING }, { "rising", GPIO_IRQF_TRIGGER_RISING }, { "both", GPIO_IRQF_TRIGGER_BOTH }, |
0eb4c6c26 gpio: move sysfs ... |
220 |
}; |
6beac9d1a gpio: sysfs: use ... |
221 |
static ssize_t edge_show(struct device *dev, |
0eb4c6c26 gpio: move sysfs ... |
222 223 |
struct device_attribute *attr, char *buf) { |
c43960fbc gpio: sysfs: add ... |
224 |
struct gpiod_data *data = dev_get_drvdata(dev); |
f0b7866a0 gpio: sysfs: remo... |
225 226 |
ssize_t status = 0; int i; |
0eb4c6c26 gpio: move sysfs ... |
227 |
|
6ffcb7971 gpio: sysfs: use ... |
228 |
mutex_lock(&data->mutex); |
0eb4c6c26 gpio: move sysfs ... |
229 |
|
f0b7866a0 gpio: sysfs: remo... |
230 |
for (i = 0; i < ARRAY_SIZE(trigger_types); i++) { |
cef1717b7 gpio: sysfs: move... |
231 |
if (data->irq_flags == trigger_types[i].flags) { |
f0b7866a0 gpio: sysfs: remo... |
232 233 234 235 |
status = sprintf(buf, "%s ", trigger_types[i].name); break; } |
0eb4c6c26 gpio: move sysfs ... |
236 |
} |
6ffcb7971 gpio: sysfs: use ... |
237 |
mutex_unlock(&data->mutex); |
0eb4c6c26 gpio: move sysfs ... |
238 239 |
return status; } |
6beac9d1a gpio: sysfs: use ... |
240 |
static ssize_t edge_store(struct device *dev, |
0eb4c6c26 gpio: move sysfs ... |
241 242 |
struct device_attribute *attr, const char *buf, size_t size) { |
b91e18076 gpio: sysfs: only... |
243 |
struct gpiod_data *data = dev_get_drvdata(dev); |
cef1717b7 gpio: sysfs: move... |
244 |
unsigned char flags; |
2ec74a959 gpio: sysfs: spli... |
245 |
ssize_t status = size; |
e4339ce32 gpio: sysfs: clea... |
246 |
int i; |
0eb4c6c26 gpio: move sysfs ... |
247 |
|
e4339ce32 gpio: sysfs: clea... |
248 |
for (i = 0; i < ARRAY_SIZE(trigger_types); i++) { |
0eb4c6c26 gpio: move sysfs ... |
249 |
if (sysfs_streq(trigger_types[i].name, buf)) |
e4339ce32 gpio: sysfs: clea... |
250 251 252 253 254 |
break; } if (i == ARRAY_SIZE(trigger_types)) return -EINVAL; |
0eb4c6c26 gpio: move sysfs ... |
255 |
|
b91e18076 gpio: sysfs: only... |
256 |
flags = trigger_types[i].flags; |
6ffcb7971 gpio: sysfs: use ... |
257 |
mutex_lock(&data->mutex); |
0eb4c6c26 gpio: move sysfs ... |
258 |
|
cef1717b7 gpio: sysfs: move... |
259 |
if (flags == data->irq_flags) { |
b91e18076 gpio: sysfs: only... |
260 261 262 |
status = size; goto out_unlock; } |
cef1717b7 gpio: sysfs: move... |
263 |
if (data->irq_flags) |
2ec74a959 gpio: sysfs: spli... |
264 265 266 267 268 269 270 |
gpio_sysfs_free_irq(dev); if (flags) { status = gpio_sysfs_request_irq(dev, flags); if (!status) status = size; } |
0eb4c6c26 gpio: move sysfs ... |
271 |
|
b91e18076 gpio: sysfs: only... |
272 |
out_unlock: |
6ffcb7971 gpio: sysfs: use ... |
273 |
mutex_unlock(&data->mutex); |
0eb4c6c26 gpio: move sysfs ... |
274 275 276 |
return status; } |
6beac9d1a gpio: sysfs: use ... |
277 |
static DEVICE_ATTR_RW(edge); |
0eb4c6c26 gpio: move sysfs ... |
278 |
|
6ffcb7971 gpio: sysfs: use ... |
279 |
/* Caller holds gpiod-data mutex. */ |
2f323b856 gpio: sysfs: rena... |
280 |
static int gpio_sysfs_set_active_low(struct device *dev, int value) |
0eb4c6c26 gpio: move sysfs ... |
281 |
{ |
0f6285080 gpio: sysfs: remo... |
282 283 |
struct gpiod_data *data = dev_get_drvdata(dev); struct gpio_desc *desc = data->desc; |
0eb4c6c26 gpio: move sysfs ... |
284 |
int status = 0; |
cef1717b7 gpio: sysfs: move... |
285 |
unsigned int flags = data->irq_flags; |
0eb4c6c26 gpio: move sysfs ... |
286 287 288 289 290 291 292 293 294 295 |
if (!!test_bit(FLAG_ACTIVE_LOW, &desc->flags) == !!value) return 0; if (value) set_bit(FLAG_ACTIVE_LOW, &desc->flags); else clear_bit(FLAG_ACTIVE_LOW, &desc->flags); /* reconfigure poll(2) support if enabled on one edge only */ |
cef1717b7 gpio: sysfs: move... |
296 297 |
if (flags == GPIO_IRQF_TRIGGER_FALLING || flags == GPIO_IRQF_TRIGGER_RISING) { |
2ec74a959 gpio: sysfs: spli... |
298 |
gpio_sysfs_free_irq(dev); |
cef1717b7 gpio: sysfs: move... |
299 |
status = gpio_sysfs_request_irq(dev, flags); |
0eb4c6c26 gpio: move sysfs ... |
300 301 302 303 |
} return status; } |
6beac9d1a gpio: sysfs: use ... |
304 |
static ssize_t active_low_show(struct device *dev, |
0eb4c6c26 gpio: move sysfs ... |
305 306 |
struct device_attribute *attr, char *buf) { |
c43960fbc gpio: sysfs: add ... |
307 308 |
struct gpiod_data *data = dev_get_drvdata(dev); struct gpio_desc *desc = data->desc; |
0eb4c6c26 gpio: move sysfs ... |
309 |
ssize_t status; |
6ffcb7971 gpio: sysfs: use ... |
310 |
mutex_lock(&data->mutex); |
0eb4c6c26 gpio: move sysfs ... |
311 |
|
f0b7866a0 gpio: sysfs: remo... |
312 313 |
status = sprintf(buf, "%d ", |
0eb4c6c26 gpio: move sysfs ... |
314 |
!!test_bit(FLAG_ACTIVE_LOW, &desc->flags)); |
6ffcb7971 gpio: sysfs: use ... |
315 |
mutex_unlock(&data->mutex); |
0eb4c6c26 gpio: move sysfs ... |
316 317 318 |
return status; } |
6beac9d1a gpio: sysfs: use ... |
319 |
static ssize_t active_low_store(struct device *dev, |
0eb4c6c26 gpio: move sysfs ... |
320 321 |
struct device_attribute *attr, const char *buf, size_t size) { |
6ffcb7971 gpio: sysfs: use ... |
322 |
struct gpiod_data *data = dev_get_drvdata(dev); |
0eb4c6c26 gpio: move sysfs ... |
323 |
ssize_t status; |
f0b7866a0 gpio: sysfs: remo... |
324 |
long value; |
0eb4c6c26 gpio: move sysfs ... |
325 |
|
6ffcb7971 gpio: sysfs: use ... |
326 |
mutex_lock(&data->mutex); |
0eb4c6c26 gpio: move sysfs ... |
327 |
|
f0b7866a0 gpio: sysfs: remo... |
328 329 |
status = kstrtol(buf, 0, &value); if (status == 0) |
2f323b856 gpio: sysfs: rena... |
330 |
status = gpio_sysfs_set_active_low(dev, value); |
0eb4c6c26 gpio: move sysfs ... |
331 |
|
6ffcb7971 gpio: sysfs: use ... |
332 |
mutex_unlock(&data->mutex); |
0eb4c6c26 gpio: move sysfs ... |
333 334 335 |
return status ? : size; } |
6beac9d1a gpio: sysfs: use ... |
336 |
static DEVICE_ATTR_RW(active_low); |
0eb4c6c26 gpio: move sysfs ... |
337 |
|
ebbeba120 gpio: sysfs: fix ... |
338 339 340 |
static umode_t gpio_is_visible(struct kobject *kobj, struct attribute *attr, int n) { |
97cd738c4 gpiolib: sysfs: u... |
341 |
struct device *dev = kobj_to_dev(kobj); |
c43960fbc gpio: sysfs: add ... |
342 343 |
struct gpiod_data *data = dev_get_drvdata(dev); struct gpio_desc *desc = data->desc; |
ebbeba120 gpio: sysfs: fix ... |
344 |
umode_t mode = attr->mode; |
427fdeef5 gpio: sysfs: remo... |
345 |
bool show_direction = data->direction_can_change; |
ebbeba120 gpio: sysfs: fix ... |
346 347 348 349 350 351 352 353 354 355 356 357 358 |
if (attr == &dev_attr_direction.attr) { if (!show_direction) mode = 0; } else if (attr == &dev_attr_edge.attr) { if (gpiod_to_irq(desc) < 0) mode = 0; if (!show_direction && test_bit(FLAG_IS_OUT, &desc->flags)) mode = 0; } return mode; } |
0915e6feb gpio: sysfs: fix ... |
359 |
static struct attribute *gpio_attrs[] = { |
ebbeba120 gpio: sysfs: fix ... |
360 361 |
&dev_attr_direction.attr, &dev_attr_edge.attr, |
0eb4c6c26 gpio: move sysfs ... |
362 363 364 365 |
&dev_attr_value.attr, &dev_attr_active_low.attr, NULL, }; |
ebbeba120 gpio: sysfs: fix ... |
366 367 368 369 370 371 372 373 374 375 |
static const struct attribute_group gpio_group = { .attrs = gpio_attrs, .is_visible = gpio_is_visible, }; static const struct attribute_group *gpio_groups[] = { &gpio_group, NULL }; |
0eb4c6c26 gpio: move sysfs ... |
376 377 378 379 380 381 382 |
/* * /sys/class/gpio/gpiochipN/ * /base ... matching gpio_chip.base (N) * /label ... matching gpio_chip.label * /ngpio ... matching gpio_chip.ngpio */ |
6beac9d1a gpio: sysfs: use ... |
383 |
static ssize_t base_show(struct device *dev, |
0eb4c6c26 gpio: move sysfs ... |
384 385 386 387 388 389 390 |
struct device_attribute *attr, char *buf) { const struct gpio_chip *chip = dev_get_drvdata(dev); return sprintf(buf, "%d ", chip->base); } |
6beac9d1a gpio: sysfs: use ... |
391 |
static DEVICE_ATTR_RO(base); |
0eb4c6c26 gpio: move sysfs ... |
392 |
|
6beac9d1a gpio: sysfs: use ... |
393 |
static ssize_t label_show(struct device *dev, |
0eb4c6c26 gpio: move sysfs ... |
394 395 396 397 398 399 400 |
struct device_attribute *attr, char *buf) { const struct gpio_chip *chip = dev_get_drvdata(dev); return sprintf(buf, "%s ", chip->label ? : ""); } |
6beac9d1a gpio: sysfs: use ... |
401 |
static DEVICE_ATTR_RO(label); |
0eb4c6c26 gpio: move sysfs ... |
402 |
|
6beac9d1a gpio: sysfs: use ... |
403 |
static ssize_t ngpio_show(struct device *dev, |
0eb4c6c26 gpio: move sysfs ... |
404 405 406 407 408 409 410 |
struct device_attribute *attr, char *buf) { const struct gpio_chip *chip = dev_get_drvdata(dev); return sprintf(buf, "%u ", chip->ngpio); } |
6beac9d1a gpio: sysfs: use ... |
411 |
static DEVICE_ATTR_RO(ngpio); |
0eb4c6c26 gpio: move sysfs ... |
412 |
|
121b6a799 gpio: sysfs: fix ... |
413 |
static struct attribute *gpiochip_attrs[] = { |
0eb4c6c26 gpio: move sysfs ... |
414 415 416 417 418 |
&dev_attr_base.attr, &dev_attr_label.attr, &dev_attr_ngpio.attr, NULL, }; |
121b6a799 gpio: sysfs: fix ... |
419 |
ATTRIBUTE_GROUPS(gpiochip); |
0eb4c6c26 gpio: move sysfs ... |
420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 |
/* * /sys/class/gpio/export ... write-only * integer N ... number of GPIO to export (full access) * /sys/class/gpio/unexport ... write-only * integer N ... number of GPIO to unexport */ static ssize_t export_store(struct class *class, struct class_attribute *attr, const char *buf, size_t len) { long gpio; struct gpio_desc *desc; int status; status = kstrtol(buf, 0, &gpio); if (status < 0) goto done; |
f13a0b0bb gpio: Get rid of ... |
438 |
desc = gpio_to_desc(gpio); |
0eb4c6c26 gpio: move sysfs ... |
439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 |
/* reject invalid GPIOs */ if (!desc) { pr_warn("%s: invalid GPIO %ld ", __func__, gpio); return -EINVAL; } /* No extra locking here; FLAG_SYSFS just signifies that the * request and export were done by on behalf of userspace, so * they may be undone on its behalf too. */ status = gpiod_request(desc, "sysfs"); if (status < 0) { if (status == -EPROBE_DEFER) status = -ENODEV; goto done; } |
e10f72bf4 gpio: gpiolib: Ge... |
457 458 459 460 461 462 463 464 465 |
status = gpiod_set_transitory(desc, false); if (!status) { status = gpiod_export(desc, true); if (status < 0) gpiod_free(desc); else set_bit(FLAG_SYSFS, &desc->flags); } |
0eb4c6c26 gpio: move sysfs ... |
466 467 468 469 470 471 472 |
done: if (status) pr_debug("%s: status %d ", __func__, status); return status ? : len; } |
d83bb159f gpio: use class_g... |
473 |
static CLASS_ATTR_WO(export); |
0eb4c6c26 gpio: move sysfs ... |
474 475 476 477 478 479 480 481 482 483 484 485 |
static ssize_t unexport_store(struct class *class, struct class_attribute *attr, const char *buf, size_t len) { long gpio; struct gpio_desc *desc; int status; status = kstrtol(buf, 0, &gpio); if (status < 0) goto done; |
f13a0b0bb gpio: Get rid of ... |
486 |
desc = gpio_to_desc(gpio); |
0eb4c6c26 gpio: move sysfs ... |
487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 |
/* reject bogus commands (gpio_unexport ignores them) */ if (!desc) { pr_warn("%s: invalid GPIO %ld ", __func__, gpio); return -EINVAL; } status = -EINVAL; /* No extra locking here; FLAG_SYSFS just signifies that the * request and export were done by on behalf of userspace, so * they may be undone on its behalf too. */ if (test_and_clear_bit(FLAG_SYSFS, &desc->flags)) { status = 0; gpiod_free(desc); } done: if (status) pr_debug("%s: status %d ", __func__, status); return status ? : len; } |
d83bb159f gpio: use class_g... |
510 |
static CLASS_ATTR_WO(unexport); |
0eb4c6c26 gpio: move sysfs ... |
511 |
|
d83bb159f gpio: use class_g... |
512 513 514 515 |
static struct attribute *gpio_class_attrs[] = { &class_attr_export.attr, &class_attr_unexport.attr, NULL, |
0eb4c6c26 gpio: move sysfs ... |
516 |
}; |
d83bb159f gpio: use class_g... |
517 |
ATTRIBUTE_GROUPS(gpio_class); |
0eb4c6c26 gpio: move sysfs ... |
518 519 520 521 |
static struct class gpio_class = { .name = "gpio", .owner = THIS_MODULE, |
d83bb159f gpio: use class_g... |
522 |
.class_groups = gpio_class_groups, |
0eb4c6c26 gpio: move sysfs ... |
523 524 525 526 527 |
}; /** * gpiod_export - export a GPIO through sysfs |
2d9d05192 gpio: sysfs: Fixu... |
528 529 |
* @desc: GPIO to make available, already requested * @direction_may_change: true if userspace may change GPIO direction |
0eb4c6c26 gpio: move sysfs ... |
530 531 532 533 534 535 536 537 538 539 540 541 542 |
* Context: arch_initcall or later * * When drivers want to make a GPIO accessible to userspace after they * have requested it -- perhaps while debugging, or as part of their * public interface -- they may use this routine. If the GPIO can * change direction (some can't) and the caller allows it, userspace * will see "direction" sysfs attribute which may be used to change * the gpio's direction. A "value" attribute will always be provided. * * Returns zero on success, else an error. */ int gpiod_export(struct gpio_desc *desc, bool direction_may_change) { |
483d82110 gpio: sysfs: fix ... |
543 |
struct gpio_chip *chip; |
ff2b13592 gpio: make the gp... |
544 |
struct gpio_device *gdev; |
c43960fbc gpio: sysfs: add ... |
545 |
struct gpiod_data *data; |
0eb4c6c26 gpio: move sysfs ... |
546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 |
unsigned long flags; int status; const char *ioname = NULL; struct device *dev; int offset; /* can't export until sysfs is available ... */ if (!gpio_class.p) { pr_debug("%s: called too early! ", __func__); return -ENOENT; } if (!desc) { pr_debug("%s: invalid gpio descriptor ", __func__); return -EINVAL; } |
fdeb8e154 gpio: reflect bas... |
564 565 |
gdev = desc->gdev; chip = gdev->chip; |
483d82110 gpio: sysfs: fix ... |
566 |
|
0eb4c6c26 gpio: move sysfs ... |
567 |
mutex_lock(&sysfs_lock); |
483d82110 gpio: sysfs: fix ... |
568 |
/* check if chip is being removed */ |
afbc4f312 gpio: move sysfs ... |
569 |
if (!chip || !gdev->mockdev) { |
483d82110 gpio: sysfs: fix ... |
570 |
status = -ENODEV; |
c43960fbc gpio: sysfs: add ... |
571 |
goto err_unlock; |
483d82110 gpio: sysfs: fix ... |
572 |
} |
0eb4c6c26 gpio: move sysfs ... |
573 574 575 576 577 578 579 580 581 582 |
spin_lock_irqsave(&gpio_lock, flags); if (!test_bit(FLAG_REQUESTED, &desc->flags) || test_bit(FLAG_EXPORT, &desc->flags)) { spin_unlock_irqrestore(&gpio_lock, flags); gpiod_dbg(desc, "%s: unavailable (requested=%d, exported=%d) ", __func__, test_bit(FLAG_REQUESTED, &desc->flags), test_bit(FLAG_EXPORT, &desc->flags)); status = -EPERM; |
c43960fbc gpio: sysfs: add ... |
583 |
goto err_unlock; |
0eb4c6c26 gpio: move sysfs ... |
584 |
} |
0eb4c6c26 gpio: move sysfs ... |
585 |
spin_unlock_irqrestore(&gpio_lock, flags); |
c43960fbc gpio: sysfs: add ... |
586 587 588 589 590 591 592 |
data = kzalloc(sizeof(*data), GFP_KERNEL); if (!data) { status = -ENOMEM; goto err_unlock; } data->desc = desc; |
6ffcb7971 gpio: sysfs: use ... |
593 |
mutex_init(&data->mutex); |
427fdeef5 gpio: sysfs: remo... |
594 595 596 597 |
if (chip->direction_input && chip->direction_output) data->direction_can_change = direction_may_change; else data->direction_can_change = false; |
c43960fbc gpio: sysfs: add ... |
598 |
|
0eb4c6c26 gpio: move sysfs ... |
599 |
offset = gpio_chip_hwgpio(desc); |
cecf58ab5 gpio: sysfs: prep... |
600 601 |
if (chip->names && chip->names[offset]) ioname = chip->names[offset]; |
0eb4c6c26 gpio: move sysfs ... |
602 |
|
ff2b13592 gpio: make the gp... |
603 |
dev = device_create_with_groups(&gpio_class, &gdev->dev, |
c43960fbc gpio: sysfs: add ... |
604 |
MKDEV(0, 0), data, gpio_groups, |
0915e6feb gpio: sysfs: fix ... |
605 606 |
ioname ? ioname : "gpio%u", desc_to_gpio(desc)); |
0eb4c6c26 gpio: move sysfs ... |
607 608 |
if (IS_ERR(dev)) { status = PTR_ERR(dev); |
c43960fbc gpio: sysfs: add ... |
609 |
goto err_free_data; |
0eb4c6c26 gpio: move sysfs ... |
610 |
} |
0eb4c6c26 gpio: move sysfs ... |
611 612 613 |
set_bit(FLAG_EXPORT, &desc->flags); mutex_unlock(&sysfs_lock); return 0; |
c43960fbc gpio: sysfs: add ... |
614 615 616 |
err_free_data: kfree(data); err_unlock: |
0eb4c6c26 gpio: move sysfs ... |
617 618 619 620 621 622 |
mutex_unlock(&sysfs_lock); gpiod_dbg(desc, "%s: status %d ", __func__, status); return status; } EXPORT_SYMBOL_GPL(gpiod_export); |
c43960fbc gpio: sysfs: add ... |
623 |
static int match_export(struct device *dev, const void *desc) |
0eb4c6c26 gpio: move sysfs ... |
624 |
{ |
c43960fbc gpio: sysfs: add ... |
625 626 627 |
struct gpiod_data *data = dev_get_drvdata(dev); return data->desc == desc; |
0eb4c6c26 gpio: move sysfs ... |
628 629 630 631 632 633 |
} /** * gpiod_export_link - create a sysfs link to an exported GPIO node * @dev: device under which to create symlink * @name: name of the symlink |
2d9d05192 gpio: sysfs: Fixu... |
634 |
* @desc: GPIO to create symlink to, already exported |
0eb4c6c26 gpio: move sysfs ... |
635 636 637 638 639 640 641 642 643 |
* * Set up a symlink from /sys/.../dev/name to /sys/class/gpio/gpioN * node. Caller is responsible for unlinking. * * Returns zero on success, else an error. */ int gpiod_export_link(struct device *dev, const char *name, struct gpio_desc *desc) { |
56d30ec14 gpio: sysfs: clea... |
644 645 |
struct device *cdev; int ret; |
0eb4c6c26 gpio: move sysfs ... |
646 647 648 649 650 651 |
if (!desc) { pr_warn("%s: invalid GPIO ", __func__); return -EINVAL; } |
56d30ec14 gpio: sysfs: clea... |
652 653 654 |
cdev = class_find_device(&gpio_class, NULL, desc, match_export); if (!cdev) return -ENODEV; |
0eb4c6c26 gpio: move sysfs ... |
655 |
|
56d30ec14 gpio: sysfs: clea... |
656 657 |
ret = sysfs_create_link(&dev->kobj, &cdev->kobj, name); put_device(cdev); |
0eb4c6c26 gpio: move sysfs ... |
658 |
|
56d30ec14 gpio: sysfs: clea... |
659 |
return ret; |
0eb4c6c26 gpio: move sysfs ... |
660 661 662 663 |
} EXPORT_SYMBOL_GPL(gpiod_export_link); /** |
31963eb03 gpio: fix documen... |
664 |
* gpiod_unexport - reverse effect of gpiod_export() |
2d9d05192 gpio: sysfs: Fixu... |
665 |
* @desc: GPIO to make unavailable |
0eb4c6c26 gpio: move sysfs ... |
666 |
* |
31963eb03 gpio: fix documen... |
667 |
* This is implicit on gpiod_free(). |
0eb4c6c26 gpio: move sysfs ... |
668 669 670 |
*/ void gpiod_unexport(struct gpio_desc *desc) { |
72eba6f66 gpio: sysfs: fix ... |
671 672 |
struct gpiod_data *data; struct device *dev; |
0eb4c6c26 gpio: move sysfs ... |
673 674 675 676 677 678 679 680 |
if (!desc) { pr_warn("%s: invalid GPIO ", __func__); return; } mutex_lock(&sysfs_lock); |
72eba6f66 gpio: sysfs: fix ... |
681 682 |
if (!test_bit(FLAG_EXPORT, &desc->flags)) goto err_unlock; |
0eb4c6c26 gpio: move sysfs ... |
683 |
|
72eba6f66 gpio: sysfs: fix ... |
684 685 686 687 688 |
dev = class_find_device(&gpio_class, NULL, desc, match_export); if (!dev) goto err_unlock; data = dev_get_drvdata(dev); |
72eba6f66 gpio: sysfs: fix ... |
689 690 691 692 693 694 695 |
clear_bit(FLAG_EXPORT, &desc->flags); device_unregister(dev); /* * Release irq after deregistration to prevent race with edge_store. */ |
cef1717b7 gpio: sysfs: move... |
696 |
if (data->irq_flags) |
72eba6f66 gpio: sysfs: fix ... |
697 |
gpio_sysfs_free_irq(dev); |
0eb4c6c26 gpio: move sysfs ... |
698 699 |
mutex_unlock(&sysfs_lock); |
72eba6f66 gpio: sysfs: fix ... |
700 701 |
put_device(dev); kfree(data); |
0eb4c6c26 gpio: move sysfs ... |
702 |
|
72eba6f66 gpio: sysfs: fix ... |
703 704 705 706 |
return; err_unlock: mutex_unlock(&sysfs_lock); |
0eb4c6c26 gpio: move sysfs ... |
707 708 |
} EXPORT_SYMBOL_GPL(gpiod_unexport); |
afbc4f312 gpio: move sysfs ... |
709 |
int gpiochip_sysfs_register(struct gpio_device *gdev) |
0eb4c6c26 gpio: move sysfs ... |
710 |
{ |
0eb4c6c26 gpio: move sysfs ... |
711 |
struct device *dev; |
d27c17285 gpio: fix abi reg... |
712 |
struct device *parent; |
afbc4f312 gpio: move sysfs ... |
713 |
struct gpio_chip *chip = gdev->chip; |
0eb4c6c26 gpio: move sysfs ... |
714 |
|
426577bd8 gpio: sysfs: rena... |
715 716 |
/* * Many systems add gpio chips for SOC support very early, |
0eb4c6c26 gpio: move sysfs ... |
717 |
* before driver model support is available. In those cases we |
426577bd8 gpio: sysfs: rena... |
718 |
* register later, in gpiolib_sysfs_init() ... here we just |
0eb4c6c26 gpio: move sysfs ... |
719 720 721 722 |
* verify that _some_ field of gpio_class got initialized. */ if (!gpio_class.p) return 0; |
d27c17285 gpio: fix abi reg... |
723 724 725 726 727 728 729 730 |
/* * For sysfs backward compatibility we need to preserve this * preferred parenting to the gpio_chip parent field, if set. */ if (chip->parent) parent = chip->parent; else parent = &gdev->dev; |
0eb4c6c26 gpio: move sysfs ... |
731 |
/* use chip->base for the ID; it's already known to be unique */ |
ddd8891e0 gpiolib: Add GPIO... |
732 733 734 |
dev = device_create_with_groups(&gpio_class, parent, MKDEV(0, 0), chip, gpiochip_groups, GPIOCHIP_NAME "%d", chip->base); |
121b6a799 gpio: sysfs: fix ... |
735 |
if (IS_ERR(dev)) |
6a4b6b0a3 gpio: sysfs: clea... |
736 |
return PTR_ERR(dev); |
3ff74be5c gpio: sysfs: redu... |
737 738 |
mutex_lock(&sysfs_lock); |
afbc4f312 gpio: move sysfs ... |
739 |
gdev->mockdev = dev; |
0eb4c6c26 gpio: move sysfs ... |
740 |
mutex_unlock(&sysfs_lock); |
6a4b6b0a3 gpio: sysfs: clea... |
741 |
return 0; |
0eb4c6c26 gpio: move sysfs ... |
742 |
} |
afbc4f312 gpio: move sysfs ... |
743 |
void gpiochip_sysfs_unregister(struct gpio_device *gdev) |
0eb4c6c26 gpio: move sysfs ... |
744 |
{ |
483d82110 gpio: sysfs: fix ... |
745 |
struct gpio_desc *desc; |
afbc4f312 gpio: move sysfs ... |
746 |
struct gpio_chip *chip = gdev->chip; |
483d82110 gpio: sysfs: fix ... |
747 |
unsigned int i; |
0eb4c6c26 gpio: move sysfs ... |
748 |
|
afbc4f312 gpio: move sysfs ... |
749 |
if (!gdev->mockdev) |
6a4b6b0a3 gpio: sysfs: clea... |
750 |
return; |
0eb4c6c26 gpio: move sysfs ... |
751 |
|
afbc4f312 gpio: move sysfs ... |
752 |
device_unregister(gdev->mockdev); |
6a4b6b0a3 gpio: sysfs: clea... |
753 754 755 |
/* prevent further gpiod exports */ mutex_lock(&sysfs_lock); |
afbc4f312 gpio: move sysfs ... |
756 |
gdev->mockdev = NULL; |
6a4b6b0a3 gpio: sysfs: clea... |
757 |
mutex_unlock(&sysfs_lock); |
483d82110 gpio: sysfs: fix ... |
758 759 760 |
/* unregister gpiod class devices owned by sysfs */ for (i = 0; i < chip->ngpio; i++) { |
fdeb8e154 gpio: reflect bas... |
761 |
desc = &gdev->descs[i]; |
483d82110 gpio: sysfs: fix ... |
762 763 764 |
if (test_and_clear_bit(FLAG_SYSFS, &desc->flags)) gpiod_free(desc); } |
0eb4c6c26 gpio: move sysfs ... |
765 766 767 768 769 770 |
} static int __init gpiolib_sysfs_init(void) { int status; unsigned long flags; |
ff2b13592 gpio: make the gp... |
771 |
struct gpio_device *gdev; |
0eb4c6c26 gpio: move sysfs ... |
772 773 774 775 776 777 778 779 780 781 782 783 |
status = class_register(&gpio_class); if (status < 0) return status; /* Scan and register the gpio_chips which registered very * early (e.g. before the class_register above was called). * * We run before arch_initcall() so chip->dev nodes can have * registered, and so arch_initcall() can always gpio_export(). */ spin_lock_irqsave(&gpio_lock, flags); |
ff2b13592 gpio: make the gp... |
784 |
list_for_each_entry(gdev, &gpio_devices, list) { |
afbc4f312 gpio: move sysfs ... |
785 |
if (gdev->mockdev) |
0eb4c6c26 gpio: move sysfs ... |
786 |
continue; |
14141a935 gpio: simplify gp... |
787 |
/* |
426577bd8 gpio: sysfs: rena... |
788 789 790 |
* TODO we yield gpio_lock here because * gpiochip_sysfs_register() acquires a mutex. This is unsafe * and needs to be fixed. |
14141a935 gpio: simplify gp... |
791 792 793 794 795 |
* * Also it would be nice to use gpiochip_find() here so we * can keep gpio_chips local to gpiolib.c, but the yield of * gpio_lock prevents us from doing this. */ |
0eb4c6c26 gpio: move sysfs ... |
796 |
spin_unlock_irqrestore(&gpio_lock, flags); |
afbc4f312 gpio: move sysfs ... |
797 |
status = gpiochip_sysfs_register(gdev); |
0eb4c6c26 gpio: move sysfs ... |
798 799 800 |
spin_lock_irqsave(&gpio_lock, flags); } spin_unlock_irqrestore(&gpio_lock, flags); |
0eb4c6c26 gpio: move sysfs ... |
801 802 803 |
return status; } postcore_initcall(gpiolib_sysfs_init); |