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