diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2016-07-26 19:16:01 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2016-07-26 19:16:01 -0700 |
commit | 1cd04d293c818687795b83cd8f2626bd4662feeb (patch) | |
tree | dcbaadd82c02204114b99c418bfae1ee57b2c4ca /tools | |
parent | 9c1958fc326a0a0a533ec8e86ea6fa30977207de (diff) | |
parent | 224f9e6d538c4cfb2fa8dc4206fceb9431271388 (diff) | |
download | lwn-1cd04d293c818687795b83cd8f2626bd4662feeb.tar.gz lwn-1cd04d293c818687795b83cd8f2626bd4662feeb.zip |
Merge tag 'gpio-v4.8-1' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-gpio
Pull GPIO updates from Linus Walleij:
"This is the bulk of GPIO changes for the v4.8 kernel cycle. The big
news is the completion of the chardev ABI which I'm very happy about
and apart from that it's an ordinary, quite busy cycle. The details
are below.
The patches are tested in linux-next for some time, patches to other
subsystem mostly have ACKs.
I got overly ambitious with configureing lines as input for IRQ lines
but it turns out that some controllers have their interrupt-enable and
input-enabling in orthogonal settings so the assumption that all IRQ
lines are input lines does not hold. Oh well, revert and back to the
drawing board with that.
Core changes:
- The big item is of course the completion of the character device
ABI. It has now replaced and surpassed the former unmaintainable
sysfs ABI: we can now hammer (bitbang) individual lines or sets of
lines and read individual lines or sets of lines from userspace,
and we can also register to listen to GPIO events from userspace.
As a tie-in we have two new tools in tools/gpio: gpio-hammer and
gpio-event-mon that illustrate the proper use of the new ABI. As
someone said: the wild west days of GPIO are now over.
- Continued to remove the pointless ARCH_[WANT_OPTIONAL|REQUIRE]_GPIOLIB
Kconfig symbols. I'm patching hexagon, openrisc, powerpc, sh,
unicore, ia64 and microblaze. These are either ACKed by their
maintainers or patched anyways after a grace period and no response
from maintainers.
Some archs (ARM) come in from their trees, and others (x86) are
still not fixed, so I might send a second pull request to root it
out later in this merge window, or just defer to v4.9.
- The GPIO tools are moved to the tools build system.
New drivers:
- New driver for the MAX77620/MAX20024.
- New driver for the Intel Merrifield.
- Enabled PCA953x for the TI PCA9536.
- Enabled PCA953x for the Intel Edison.
- Enabled R8A7792 in the RCAR driver.
Driver improvements:
- The STMPE and F7188x now supports the .get_direction() callback.
- The Xilinx driver supports setting multiple lines at once.
- ACPI support for the Vulcan GPIO controller.
- The MMIO GPIO driver supports device tree probing.
- The Acer One 10 is supported through the _DEP ACPI attribute.
Cleanups:
- A major cleanup of the OF/DT support code. It is way easier to
read and understand now, probably this improves performance too.
- Drop a few redundant .owner assignments.
- Remove CLPS711x boardfile support: we are 100% DT"
* tag 'gpio-v4.8-1' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-gpio: (67 commits)
MAINTAINERS: Add INTEL MERRIFIELD GPIO entry
gpio: dwapb: add missing fwnode_handle_put() in dwapb_gpio_get_pdata()
gpio: merrifield: Protect irq_ack() and gpio_set() by lock
gpio: merrifield: Introduce GPIO driver to support Merrifield
gpio: intel-mid: Make it depend to X86_INTEL_MID
gpio: intel-mid: Sort header block alphabetically
gpio: intel-mid: Remove potentially harmful code
gpio: rcar: add R8A7792 support
gpiolib: remove duplicated include from gpiolib.c
Revert "gpio: convince line to become input in irq helper"
gpiolib: of_find_gpio(): Don't discard errors
gpio: of: Allow overriding the device node
gpio: free handles in fringe cases
gpio: tps65218: Add platform_device_id table
gpio: max77620: get gpio value based on direction
gpio: lynxpoint: avoid potential warning on error path
tools/gpio: add install section
tools/gpio: move to tools buildsystem
gpio: intel-mid: switch to devm_gpiochip_add_data()
gpio: 74x164: Use spi_write() helper instead of open coding
...
Diffstat (limited to 'tools')
-rw-r--r-- | tools/Makefile | 7 | ||||
-rw-r--r-- | tools/gpio/Build | 3 | ||||
-rw-r--r-- | tools/gpio/Makefile | 75 | ||||
-rw-r--r-- | tools/gpio/gpio-event-mon.c | 192 | ||||
-rw-r--r-- | tools/gpio/gpio-hammer.c | 189 |
5 files changed, 457 insertions, 9 deletions
diff --git a/tools/Makefile b/tools/Makefile index f10b64d8c674..daa8fb3e4363 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -85,7 +85,7 @@ tmon: FORCE freefall: FORCE $(call descend,laptop/$@) -all: acpi cgroup cpupower hv firewire lguest \ +all: acpi cgroup cpupower gpio hv firewire lguest \ perf selftests turbostat usb \ virtio vm net x86_energy_perf_policy \ tmon freefall objtool @@ -96,7 +96,7 @@ acpi_install: cpupower_install: $(call descend,power/$(@:_install=),install) -cgroup_install firewire_install hv_install lguest_install perf_install usb_install virtio_install vm_install net_install objtool_install: +cgroup_install firewire_install gpio_install hv_install lguest_install perf_install usb_install virtio_install vm_install net_install objtool_install: $(call descend,$(@:_install=),install) selftests_install: @@ -114,7 +114,8 @@ freefall_install: kvm_stat_install: $(call descend,kvm/$(@:_install=),install) -install: acpi_install cgroup_install cpupower_install hv_install firewire_install lguest_install \ +install: acpi_install cgroup_install cpupower_install gpio_install \ + hv_install firewire_install lguest_install \ perf_install selftests_install turbostat_install usb_install \ virtio_install vm_install net_install x86_energy_perf_policy_install \ tmon_install freefall_install objtool_install kvm_stat_install diff --git a/tools/gpio/Build b/tools/gpio/Build new file mode 100644 index 000000000000..620c1937d957 --- /dev/null +++ b/tools/gpio/Build @@ -0,0 +1,3 @@ +lsgpio-y += lsgpio.o gpio-utils.o +gpio-hammer-y += gpio-hammer.o gpio-utils.o +gpio-event-mon-y += gpio-event-mon.o gpio-utils.o diff --git a/tools/gpio/Makefile b/tools/gpio/Makefile index c155d6bc47a7..250a891e6ef0 100644 --- a/tools/gpio/Makefile +++ b/tools/gpio/Makefile @@ -1,12 +1,75 @@ +include ../scripts/Makefile.include + +bindir ?= /usr/bin + +ifeq ($(srctree),) +srctree := $(patsubst %/,%,$(dir $(shell pwd))) +srctree := $(patsubst %/,%,$(dir $(srctree))) +endif + +# Do not use make's built-in rules +# (this improves performance and avoids hard-to-debug behaviour); +MAKEFLAGS += -r + CC = $(CROSS_COMPILE)gcc -CFLAGS += -O2 -Wall -g -D_GNU_SOURCE +LD = $(CROSS_COMPILE)ld +CFLAGS += -O2 -Wall -g -D_GNU_SOURCE -I$(OUTPUT)include + +ALL_TARGETS := lsgpio gpio-hammer gpio-event-mon +ALL_PROGRAMS := $(patsubst %,$(OUTPUT)%,$(ALL_TARGETS)) + +all: $(ALL_PROGRAMS) -all: lsgpio +export srctree OUTPUT CC LD CFLAGS +include $(srctree)/tools/build/Makefile.include -lsgpio: lsgpio.o gpio-utils.o +# +# We need the following to be outside of kernel tree +# +$(OUTPUT)include/linux/gpio.h: ../../include/uapi/linux/gpio.h + mkdir -p $(OUTPUT)include/linux 2>&1 || true + ln -sf $(CURDIR)/../../include/uapi/linux/gpio.h $@ -%.o: %.c gpio-utils.h +prepare: $(OUTPUT)include/linux/gpio.h + +# +# lsgpio +# +LSGPIO_IN := $(OUTPUT)lsgpio-in.o +$(LSGPIO_IN): prepare FORCE + $(Q)$(MAKE) $(build)=lsgpio +$(OUTPUT)lsgpio: $(LSGPIO_IN) + $(QUIET_LINK)$(CC) $(CFLAGS) $(LDFLAGS) $< -o $@ + +# +# gpio-hammer +# +GPIO_HAMMER_IN := $(OUTPUT)gpio-hammer-in.o +$(GPIO_HAMMER_IN): prepare FORCE + $(Q)$(MAKE) $(build)=gpio-hammer +$(OUTPUT)gpio-hammer: $(GPIO_HAMMER_IN) + $(QUIET_LINK)$(CC) $(CFLAGS) $(LDFLAGS) $< -o $@ + +# +# gpio-event-mon +# +GPIO_EVENT_MON_IN := $(OUTPUT)gpio-event-mon-in.o +$(GPIO_EVENT_MON_IN): prepare FORCE + $(Q)$(MAKE) $(build)=gpio-event-mon +$(OUTPUT)gpio-event-mon: $(GPIO_EVENT_MON_IN) + $(QUIET_LINK)$(CC) $(CFLAGS) $(LDFLAGS) $< -o $@ -.PHONY: clean clean: - rm -f *.o lsgpio + rm -f $(ALL_PROGRAMS) + rm -f $(OUTPUT)include/linux/gpio.h + find $(if $(OUTPUT),$(OUTPUT),.) -name '*.o' -delete -o -name '\.*.d' -delete + +install: $(ALL_PROGRAMS) + install -d -m 755 $(DESTDIR)$(bindir); \ + for program in $(ALL_PROGRAMS); do \ + install $$program $(DESTDIR)$(bindir); \ + done + +FORCE: + +.PHONY: all install clean FORCE prepare diff --git a/tools/gpio/gpio-event-mon.c b/tools/gpio/gpio-event-mon.c new file mode 100644 index 000000000000..448ed96b3b4f --- /dev/null +++ b/tools/gpio/gpio-event-mon.c @@ -0,0 +1,192 @@ +/* + * gpio-hammer - example swiss army knife to shake GPIO lines on a system + * + * Copyright (C) 2016 Linus Walleij + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * Usage: + * gpio-event-mon -n <device-name> -o <offset> + */ + +#include <unistd.h> +#include <stdlib.h> +#include <stdbool.h> +#include <stdio.h> +#include <dirent.h> +#include <errno.h> +#include <string.h> +#include <poll.h> +#include <fcntl.h> +#include <getopt.h> +#include <inttypes.h> +#include <sys/ioctl.h> +#include <linux/gpio.h> + +int monitor_device(const char *device_name, + unsigned int line, + u_int32_t handleflags, + u_int32_t eventflags, + unsigned int loops) +{ + struct gpioevent_request req; + struct gpiohandle_data data; + char *chrdev_name; + int fd; + int ret; + int i = 0; + + ret = asprintf(&chrdev_name, "/dev/%s", device_name); + if (ret < 0) + return -ENOMEM; + + fd = open(chrdev_name, 0); + if (fd == -1) { + ret = -errno; + fprintf(stderr, "Failed to open %s\n", chrdev_name); + goto exit_close_error; + } + + req.lineoffset = line; + req.handleflags = handleflags; + req.eventflags = eventflags; + strcpy(req.consumer_label, "gpio-event-mon"); + + ret = ioctl(fd, GPIO_GET_LINEEVENT_IOCTL, &req); + if (ret == -1) { + ret = -errno; + fprintf(stderr, "Failed to issue GET EVENT " + "IOCTL (%d)\n", + ret); + goto exit_close_error; + } + + /* Read initial states */ + ret = ioctl(req.fd, GPIOHANDLE_GET_LINE_VALUES_IOCTL, &data); + if (ret == -1) { + ret = -errno; + fprintf(stderr, "Failed to issue GPIOHANDLE GET LINE " + "VALUES IOCTL (%d)\n", + ret); + goto exit_close_error; + } + + fprintf(stdout, "Monitoring line %d on %s\n", line, device_name); + fprintf(stdout, "Initial line value: %d\n", data.values[0]); + + while (1) { + struct gpioevent_data event; + + ret = read(req.fd, &event, sizeof(event)); + if (ret == -1) { + if (errno == -EAGAIN) { + fprintf(stderr, "nothing available\n"); + continue; + } else { + ret = -errno; + fprintf(stderr, "Failed to read event (%d)\n", + ret); + break; + } + } + + if (ret != sizeof(event)) { + fprintf(stderr, "Reading event failed\n"); + ret = -EIO; + break; + } + fprintf(stdout, "GPIO EVENT %" PRIu64 ": ", event.timestamp); + switch (event.id) { + case GPIOEVENT_EVENT_RISING_EDGE: + fprintf(stdout, "rising edge"); + break; + case GPIOEVENT_EVENT_FALLING_EDGE: + fprintf(stdout, "falling edge"); + break; + default: + fprintf(stdout, "unknown event"); + } + fprintf(stdout, "\n"); + + i++; + if (i == loops) + break; + } + +exit_close_error: + if (close(fd) == -1) + perror("Failed to close GPIO character device file"); + free(chrdev_name); + return ret; +} + +void print_usage(void) +{ + fprintf(stderr, "Usage: gpio-event-mon [options]...\n" + "Listen to events on GPIO lines, 0->1 1->0\n" + " -n <name> Listen on GPIOs on a named device (must be stated)\n" + " -o <n> Offset to monitor\n" + " -d Set line as open drain\n" + " -s Set line as open source\n" + " -r Listen for rising edges\n" + " -f Listen for falling edges\n" + " [-c <n>] Do <n> loops (optional, infinite loop if not stated)\n" + " -? This helptext\n" + "\n" + "Example:\n" + "gpio-event-mon -n gpiochip0 -o 4 -r -f\n" + ); +} + +int main(int argc, char **argv) +{ + const char *device_name = NULL; + unsigned int line = -1; + unsigned int loops = 0; + u_int32_t handleflags = GPIOHANDLE_REQUEST_INPUT; + u_int32_t eventflags = 0; + int c; + + while ((c = getopt(argc, argv, "c:n:o:dsrf?")) != -1) { + switch (c) { + case 'c': + loops = strtoul(optarg, NULL, 10); + break; + case 'n': + device_name = optarg; + break; + case 'o': + line = strtoul(optarg, NULL, 10); + break; + case 'd': + handleflags |= GPIOHANDLE_REQUEST_OPEN_DRAIN; + break; + case 's': + handleflags |= GPIOHANDLE_REQUEST_OPEN_SOURCE; + break; + case 'r': + eventflags |= GPIOEVENT_REQUEST_RISING_EDGE; + break; + case 'f': + eventflags |= GPIOEVENT_REQUEST_FALLING_EDGE; + break; + case '?': + print_usage(); + return -1; + } + } + + if (!device_name || line == -1) { + print_usage(); + return -1; + } + if (!eventflags) { + printf("No flags specified, listening on both rising and " + "falling edges\n"); + eventflags = GPIOEVENT_REQUEST_BOTH_EDGES; + } + return monitor_device(device_name, line, handleflags, + eventflags, loops); +} diff --git a/tools/gpio/gpio-hammer.c b/tools/gpio/gpio-hammer.c new file mode 100644 index 000000000000..37b3f141053d --- /dev/null +++ b/tools/gpio/gpio-hammer.c @@ -0,0 +1,189 @@ +/* + * gpio-hammer - example swiss army knife to shake GPIO lines on a system + * + * Copyright (C) 2016 Linus Walleij + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * Usage: + * gpio-hammer -n <device-name> -o <offset1> -o <offset2> + */ + +#include <unistd.h> +#include <stdlib.h> +#include <stdbool.h> +#include <stdio.h> +#include <dirent.h> +#include <errno.h> +#include <string.h> +#include <poll.h> +#include <fcntl.h> +#include <getopt.h> +#include <sys/ioctl.h> +#include <linux/gpio.h> + +int hammer_device(const char *device_name, unsigned int *lines, int nlines, + unsigned int loops) +{ + struct gpiohandle_request req; + struct gpiohandle_data data; + char *chrdev_name; + char swirr[] = "-\\|/"; + int fd; + int ret; + int i, j; + unsigned int iteration = 0; + + ret = asprintf(&chrdev_name, "/dev/%s", device_name); + if (ret < 0) + return -ENOMEM; + + fd = open(chrdev_name, 0); + if (fd == -1) { + ret = -errno; + fprintf(stderr, "Failed to open %s\n", chrdev_name); + goto exit_close_error; + } + + /* Request lines as output */ + for (i = 0; i < nlines; i++) + req.lineoffsets[i] = lines[i]; + req.flags = GPIOHANDLE_REQUEST_OUTPUT; /* Request as output */ + strcpy(req.consumer_label, "gpio-hammer"); + req.lines = nlines; + ret = ioctl(fd, GPIO_GET_LINEHANDLE_IOCTL, &req); + if (ret == -1) { + ret = -errno; + fprintf(stderr, "Failed to issue GET LINEHANDLE " + "IOCTL (%d)\n", + ret); + goto exit_close_error; + } + + /* Read initial states */ + ret = ioctl(req.fd, GPIOHANDLE_GET_LINE_VALUES_IOCTL, &data); + if (ret == -1) { + ret = -errno; + fprintf(stderr, "Failed to issue GPIOHANDLE GET LINE " + "VALUES IOCTL (%d)\n", + ret); + goto exit_close_error; + } + fprintf(stdout, "Hammer lines ["); + for (i = 0; i < nlines; i++) { + fprintf(stdout, "%d", lines[i]); + if (i != (nlines - 1)) + fprintf(stdout, ", "); + } + fprintf(stdout, "] on %s, initial states: [", device_name); + for (i = 0; i < nlines; i++) { + fprintf(stdout, "%d", data.values[i]); + if (i != (nlines - 1)) + fprintf(stdout, ", "); + } + fprintf(stdout, "]\n"); + + /* Hammertime! */ + j = 0; + while (1) { + /* Invert all lines so we blink */ + for (i = 0; i < nlines; i++) + data.values[i] = !data.values[i]; + + ret = ioctl(req.fd, GPIOHANDLE_SET_LINE_VALUES_IOCTL, &data); + if (ret == -1) { + ret = -errno; + fprintf(stderr, "Failed to issue GPIOHANDLE SET LINE " + "VALUES IOCTL (%d)\n", + ret); + goto exit_close_error; + } + /* Re-read values to get status */ + ret = ioctl(req.fd, GPIOHANDLE_GET_LINE_VALUES_IOCTL, &data); + if (ret == -1) { + ret = -errno; + fprintf(stderr, "Failed to issue GPIOHANDLE GET LINE " + "VALUES IOCTL (%d)\n", + ret); + goto exit_close_error; + } + + fprintf(stdout, "[%c] ", swirr[j]); + j++; + if (j == sizeof(swirr)-1) + j = 0; + + fprintf(stdout, "["); + for (i = 0; i < nlines; i++) { + fprintf(stdout, "%d: %d", lines[i], data.values[i]); + if (i != (nlines - 1)) + fprintf(stdout, ", "); + } + fprintf(stdout, "]\r"); + fflush(stdout); + sleep(1); + iteration++; + if (loops && iteration == loops) + break; + } + fprintf(stdout, "\n"); + ret = 0; + +exit_close_error: + if (close(fd) == -1) + perror("Failed to close GPIO character device file"); + free(chrdev_name); + return ret; +} + +void print_usage(void) +{ + fprintf(stderr, "Usage: gpio-hammer [options]...\n" + "Hammer GPIO lines, 0->1->0->1...\n" + " -n <name> Hammer GPIOs on a named device (must be stated)\n" + " -o <n> Offset[s] to hammer, at least one, several can be stated\n" + " [-c <n>] Do <n> loops (optional, infinite loop if not stated)\n" + " -? This helptext\n" + "\n" + "Example:\n" + "gpio-hammer -n gpiochip0 -o 4\n" + ); +} + +int main(int argc, char **argv) +{ + const char *device_name = NULL; + unsigned int lines[GPIOHANDLES_MAX]; + unsigned int loops = 0; + int nlines; + int c; + int i; + + i = 0; + while ((c = getopt(argc, argv, "c:n:o:?")) != -1) { + switch (c) { + case 'c': + loops = strtoul(optarg, NULL, 10); + break; + case 'n': + device_name = optarg; + break; + case 'o': + lines[i] = strtoul(optarg, NULL, 10); + i++; + break; + case '?': + print_usage(); + return -1; + } + } + nlines = i; + + if (!device_name || !nlines) { + print_usage(); + return -1; + } + return hammer_device(device_name, lines, nlines, loops); +} |