Commit b2929a9c authored by Linus Walleij's avatar Linus Walleij

Merge tag 'gpio-updates-for-v5.7-part1' of...

Merge tag 'gpio-updates-for-v5.7-part1' of git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux into devel

gpio updates for v5.7 part 1

- make irqs optional in gpio-pxa
- improve the logic behind the get() and set() callbacks in gpio-wcd934x
- add new kfifo helpers (acked by kfifo maintainer)
- rework the locking mechanism for lineevent kfifo
- implement a new ioctl() for watching changes on GPIO lines
parents 046e14af 33f0c47b
...@@ -652,8 +652,8 @@ static int pxa_gpio_probe(struct platform_device *pdev) ...@@ -652,8 +652,8 @@ static int pxa_gpio_probe(struct platform_device *pdev)
if (!pchip->irqdomain) if (!pchip->irqdomain)
return -ENOMEM; return -ENOMEM;
irq0 = platform_get_irq_byname(pdev, "gpio0"); irq0 = platform_get_irq_byname_optional(pdev, "gpio0");
irq1 = platform_get_irq_byname(pdev, "gpio1"); irq1 = platform_get_irq_byname_optional(pdev, "gpio1");
irq_mux = platform_get_irq_byname(pdev, "gpio_mux"); irq_mux = platform_get_irq_byname(pdev, "gpio_mux");
if ((irq0 > 0 && irq1 <= 0) || (irq0 <= 0 && irq1 > 0) if ((irq0 > 0 && irq1 <= 0) || (irq0 <= 0 && irq1 > 0)
|| (irq_mux <= 0)) || (irq_mux <= 0))
......
...@@ -57,16 +57,19 @@ static int wcd_gpio_direction_output(struct gpio_chip *chip, unsigned int pin, ...@@ -57,16 +57,19 @@ static int wcd_gpio_direction_output(struct gpio_chip *chip, unsigned int pin,
static int wcd_gpio_get(struct gpio_chip *chip, unsigned int pin) static int wcd_gpio_get(struct gpio_chip *chip, unsigned int pin)
{ {
struct wcd_gpio_data *data = gpiochip_get_data(chip); struct wcd_gpio_data *data = gpiochip_get_data(chip);
int value; unsigned int value;
regmap_read(data->map, WCD_REG_VAL_CTL_OFFSET, &value); regmap_read(data->map, WCD_REG_VAL_CTL_OFFSET, &value);
return !!(value && WCD_PIN_MASK(pin)); return !!(value & WCD_PIN_MASK(pin));
} }
static void wcd_gpio_set(struct gpio_chip *chip, unsigned int pin, int val) static void wcd_gpio_set(struct gpio_chip *chip, unsigned int pin, int val)
{ {
wcd_gpio_direction_output(chip, pin, val); struct wcd_gpio_data *data = gpiochip_get_data(chip);
regmap_update_bits(data->map, WCD_REG_VAL_CTL_OFFSET,
WCD_PIN_MASK(pin), val ? WCD_PIN_MASK(pin) : 0);
} }
static int wcd_gpio_probe(struct platform_device *pdev) static int wcd_gpio_probe(struct platform_device *pdev)
......
This diff is collapsed.
...@@ -56,6 +56,7 @@ struct gpio_device { ...@@ -56,6 +56,7 @@ struct gpio_device {
const char *label; const char *label;
void *data; void *data;
struct list_head list; struct list_head list;
struct atomic_notifier_head notifier;
#ifdef CONFIG_PINCTRL #ifdef CONFIG_PINCTRL
/* /*
......
...@@ -246,6 +246,37 @@ __kfifo_int_must_check_helper(int val) ...@@ -246,6 +246,37 @@ __kfifo_int_must_check_helper(int val)
__tmpq->kfifo.in == __tmpq->kfifo.out; \ __tmpq->kfifo.in == __tmpq->kfifo.out; \
}) })
/**
* kfifo_is_empty_spinlocked - returns true if the fifo is empty using
* a spinlock for locking
* @fifo: address of the fifo to be used
* @lock: spinlock to be used for locking
*/
#define kfifo_is_empty_spinlocked(fifo, lock) \
({ \
unsigned long __flags; \
bool __ret; \
spin_lock_irqsave(lock, __flags); \
__ret = kfifo_is_empty(fifo); \
spin_unlock_irqrestore(lock, __flags); \
__ret; \
})
/**
* kfifo_is_empty_spinlocked_noirqsave - returns true if the fifo is empty
* using a spinlock for locking, doesn't disable interrupts
* @fifo: address of the fifo to be used
* @lock: spinlock to be used for locking
*/
#define kfifo_is_empty_spinlocked_noirqsave(fifo, lock) \
({ \
bool __ret; \
spin_lock(lock); \
__ret = kfifo_is_empty(fifo); \
spin_unlock(lock); \
__ret; \
})
/** /**
* kfifo_is_full - returns true if the fifo is full * kfifo_is_full - returns true if the fifo is full
* @fifo: address of the fifo to be used * @fifo: address of the fifo to be used
...@@ -517,6 +548,26 @@ __kfifo_uint_must_check_helper( \ ...@@ -517,6 +548,26 @@ __kfifo_uint_must_check_helper( \
__ret; \ __ret; \
}) })
/**
* kfifo_in_spinlocked_noirqsave - put data into fifo using a spinlock for
* locking, don't disable interrupts
* @fifo: address of the fifo to be used
* @buf: the data to be added
* @n: number of elements to be added
* @lock: pointer to the spinlock to use for locking
*
* This is a variant of kfifo_in_spinlocked() but uses spin_lock/unlock()
* for locking and doesn't disable interrupts.
*/
#define kfifo_in_spinlocked_noirqsave(fifo, buf, n, lock) \
({ \
unsigned int __ret; \
spin_lock(lock); \
__ret = kfifo_in(fifo, buf, n); \
spin_unlock(lock); \
__ret; \
})
/* alias for kfifo_in_spinlocked, will be removed in a future release */ /* alias for kfifo_in_spinlocked, will be removed in a future release */
#define kfifo_in_locked(fifo, buf, n, lock) \ #define kfifo_in_locked(fifo, buf, n, lock) \
kfifo_in_spinlocked(fifo, buf, n, lock) kfifo_in_spinlocked(fifo, buf, n, lock)
...@@ -569,6 +620,28 @@ __kfifo_uint_must_check_helper( \ ...@@ -569,6 +620,28 @@ __kfifo_uint_must_check_helper( \
}) \ }) \
) )
/**
* kfifo_out_spinlocked_noirqsave - get data from the fifo using a spinlock
* for locking, don't disable interrupts
* @fifo: address of the fifo to be used
* @buf: pointer to the storage buffer
* @n: max. number of elements to get
* @lock: pointer to the spinlock to use for locking
*
* This is a variant of kfifo_out_spinlocked() which uses spin_lock/unlock()
* for locking and doesn't disable interrupts.
*/
#define kfifo_out_spinlocked_noirqsave(fifo, buf, n, lock) \
__kfifo_uint_must_check_helper( \
({ \
unsigned int __ret; \
spin_lock(lock); \
__ret = kfifo_out(fifo, buf, n); \
spin_unlock(lock); \
__ret; \
}) \
)
/* alias for kfifo_out_spinlocked, will be removed in a future release */ /* alias for kfifo_out_spinlocked, will be removed in a future release */
#define kfifo_out_locked(fifo, buf, n, lock) \ #define kfifo_out_locked(fifo, buf, n, lock) \
kfifo_out_spinlocked(fifo, buf, n, lock) kfifo_out_spinlocked(fifo, buf, n, lock)
......
...@@ -59,6 +59,34 @@ struct gpioline_info { ...@@ -59,6 +59,34 @@ struct gpioline_info {
/* Maximum number of requested handles */ /* Maximum number of requested handles */
#define GPIOHANDLES_MAX 64 #define GPIOHANDLES_MAX 64
/* Possible line status change events */
enum {
GPIOLINE_CHANGED_REQUESTED = 1,
GPIOLINE_CHANGED_RELEASED,
GPIOLINE_CHANGED_CONFIG,
};
/**
* struct gpioline_info_changed - Information about a change in status
* of a GPIO line
* @info: updated line information
* @timestamp: estimate of time of status change occurrence, in nanoseconds
* and GPIOLINE_CHANGED_CONFIG
* @event_type: one of GPIOLINE_CHANGED_REQUESTED, GPIOLINE_CHANGED_RELEASED
*
* Note: struct gpioline_info embedded here has 32-bit alignment on its own,
* but it works fine with 64-bit alignment too. With its 72 byte size, we can
* guarantee there are no implicit holes between it and subsequent members.
* The 20-byte padding at the end makes sure we don't add any implicit padding
* at the end of the structure on 64-bit architectures.
*/
struct gpioline_info_changed {
struct gpioline_info info;
__u64 timestamp;
__u32 event_type;
__u32 padding[5]; /* for future use */
};
/* Linerequest flags */ /* Linerequest flags */
#define GPIOHANDLE_REQUEST_INPUT (1UL << 0) #define GPIOHANDLE_REQUEST_INPUT (1UL << 0)
#define GPIOHANDLE_REQUEST_OUTPUT (1UL << 1) #define GPIOHANDLE_REQUEST_OUTPUT (1UL << 1)
...@@ -176,6 +204,8 @@ struct gpioevent_data { ...@@ -176,6 +204,8 @@ struct gpioevent_data {
#define GPIO_GET_CHIPINFO_IOCTL _IOR(0xB4, 0x01, struct gpiochip_info) #define GPIO_GET_CHIPINFO_IOCTL _IOR(0xB4, 0x01, struct gpiochip_info)
#define GPIO_GET_LINEINFO_IOCTL _IOWR(0xB4, 0x02, struct gpioline_info) #define GPIO_GET_LINEINFO_IOCTL _IOWR(0xB4, 0x02, struct gpioline_info)
#define GPIO_GET_LINEINFO_WATCH_IOCTL _IOWR(0xB4, 0x0b, struct gpioline_info)
#define GPIO_GET_LINEINFO_UNWATCH_IOCTL _IOWR(0xB4, 0x0c, __u32)
#define GPIO_GET_LINEHANDLE_IOCTL _IOWR(0xB4, 0x03, struct gpiohandle_request) #define GPIO_GET_LINEHANDLE_IOCTL _IOWR(0xB4, 0x03, struct gpiohandle_request)
#define GPIO_GET_LINEEVENT_IOCTL _IOWR(0xB4, 0x04, struct gpioevent_request) #define GPIO_GET_LINEEVENT_IOCTL _IOWR(0xB4, 0x04, struct gpioevent_request)
......
gpio-event-mon gpio-event-mon
gpio-hammer gpio-hammer
gpio-watch
lsgpio lsgpio
include/linux/gpio.h include/linux/gpio.h
...@@ -2,3 +2,4 @@ gpio-utils-y += gpio-utils.o ...@@ -2,3 +2,4 @@ gpio-utils-y += gpio-utils.o
lsgpio-y += lsgpio.o gpio-utils.o lsgpio-y += lsgpio.o gpio-utils.o
gpio-hammer-y += gpio-hammer.o gpio-utils.o gpio-hammer-y += gpio-hammer.o gpio-utils.o
gpio-event-mon-y += gpio-event-mon.o gpio-utils.o gpio-event-mon-y += gpio-event-mon.o gpio-utils.o
gpio-watch-y += gpio-watch.o
...@@ -18,7 +18,7 @@ MAKEFLAGS += -r ...@@ -18,7 +18,7 @@ MAKEFLAGS += -r
override CFLAGS += -O2 -Wall -g -D_GNU_SOURCE -I$(OUTPUT)include override CFLAGS += -O2 -Wall -g -D_GNU_SOURCE -I$(OUTPUT)include
ALL_TARGETS := lsgpio gpio-hammer gpio-event-mon ALL_TARGETS := lsgpio gpio-hammer gpio-event-mon gpio-watch
ALL_PROGRAMS := $(patsubst %,$(OUTPUT)%,$(ALL_TARGETS)) ALL_PROGRAMS := $(patsubst %,$(OUTPUT)%,$(ALL_TARGETS))
all: $(ALL_PROGRAMS) all: $(ALL_PROGRAMS)
...@@ -66,6 +66,15 @@ $(GPIO_EVENT_MON_IN): prepare FORCE $(OUTPUT)gpio-utils-in.o ...@@ -66,6 +66,15 @@ $(GPIO_EVENT_MON_IN): prepare FORCE $(OUTPUT)gpio-utils-in.o
$(OUTPUT)gpio-event-mon: $(GPIO_EVENT_MON_IN) $(OUTPUT)gpio-event-mon: $(GPIO_EVENT_MON_IN)
$(QUIET_LINK)$(CC) $(CFLAGS) $(LDFLAGS) $< -o $@ $(QUIET_LINK)$(CC) $(CFLAGS) $(LDFLAGS) $< -o $@
#
# gpio-watch
#
GPIO_WATCH_IN := $(OUTPUT)gpio-watch-in.o
$(GPIO_WATCH_IN): prepare FORCE
$(Q)$(MAKE) $(build)=gpio-watch
$(OUTPUT)gpio-watch: $(GPIO_WATCH_IN)
$(QUIET_LINK)$(CC) $(CFLAGS) $(LDFLAGS) $< -o $@
clean: clean:
rm -f $(ALL_PROGRAMS) rm -f $(ALL_PROGRAMS)
rm -f $(OUTPUT)include/linux/gpio.h rm -f $(OUTPUT)include/linux/gpio.h
......
// SPDX-License-Identifier: GPL-2.0-only
/*
* gpio-watch - monitor unrequested lines for property changes using the
* character device
*
* Copyright (C) 2019 BayLibre SAS
* Author: Bartosz Golaszewski <bgolaszewski@baylibre.com>
*/
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <linux/gpio.h>
#include <poll.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <unistd.h>
int main(int argc, char **argv)
{
struct gpioline_info_changed chg;
struct gpioline_info req;
struct pollfd pfd;
int fd, i, j, ret;
char *event, *end;
ssize_t rd;
if (argc < 3)
goto err_usage;
fd = open(argv[1], O_RDWR | O_CLOEXEC);
if (fd < 0) {
perror("unable to open gpiochip");
return EXIT_FAILURE;
}
for (i = 0, j = 2; i < argc - 2; i++, j++) {
memset(&req, 0, sizeof(req));
req.line_offset = strtoul(argv[j], &end, 0);
if (*end != '\0')
goto err_usage;
ret = ioctl(fd, GPIO_GET_LINEINFO_WATCH_IOCTL, &req);
if (ret) {
perror("unable to set up line watch");
return EXIT_FAILURE;
}
}
pfd.fd = fd;
pfd.events = POLLIN | POLLPRI;
for (;;) {
ret = poll(&pfd, 1, 5000);
if (ret < 0) {
perror("error polling the linechanged fd");
return EXIT_FAILURE;
} else if (ret > 0) {
memset(&chg, 0, sizeof(chg));
rd = read(pfd.fd, &chg, sizeof(chg));
if (rd < 0 || rd != sizeof(chg)) {
if (rd != sizeof(chg))
errno = EIO;
perror("error reading line change event");
return EXIT_FAILURE;
}
switch (chg.event_type) {
case GPIOLINE_CHANGED_REQUESTED:
event = "requested";
break;
case GPIOLINE_CHANGED_RELEASED:
event = "released";
break;
case GPIOLINE_CHANGED_CONFIG:
event = "config changed";
break;
default:
fprintf(stderr,
"invalid event type received from the kernel\n");
return EXIT_FAILURE;
}
printf("line %u: %s at %llu\n",
chg.info.line_offset, event, chg.timestamp);
}
}
return 0;
err_usage:
printf("%s: <gpiochip> <line0> <line1> ...\n", argv[0]);
return EXIT_FAILURE;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment