Module: xenomai-3 Branch: wip/drivers Commit: d0adf70c4a58c9ccc113ec54f4e07d0cc6261d5c URL: http://git.xenomai.org/?p=xenomai-3.git;a=commit;h=d0adf70c4a58c9ccc113ec54f4e07d0cc6261d5c
Author: Philippe Gerum <r...@xenomai.org> Date: Wed May 25 10:54:22 2016 +0200 drivers/gpio: introduce real-time GPIO support --- configure.ac | 1 + include/rtdm/Makefile.am | 1 + include/rtdm/gpio.h | 24 +++ include/rtdm/uapi/Makefile.am | 1 + include/rtdm/uapi/gpio.h | 28 +++ include/rtdm/uapi/rtdm.h | 1 + kernel/drivers/Kconfig | 1 + kernel/drivers/Makefile | 2 +- kernel/drivers/gpio/Kconfig | 25 +++ kernel/drivers/gpio/Makefile | 7 + kernel/drivers/gpio/gpio-bcm2708.c | 39 ++++ kernel/drivers/gpio/gpio-core.c | 349 ++++++++++++++++++++++++++++++++++++ kernel/drivers/gpio/gpio-core.h | 48 +++++ kernel/drivers/gpio/gpio-mxc.c | 67 +++++++ 14 files changed, 593 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index b33ff12..1f3c4fe 100644 --- a/configure.ac +++ b/configure.ac @@ -884,6 +884,7 @@ AC_CONFIG_FILES([ \ testsuite/Makefile \ testsuite/latency/Makefile \ testsuite/switchtest/Makefile \ + testsuite/gpiotest/Makefile \ testsuite/smokey/Makefile \ testsuite/smokey/arith/Makefile \ testsuite/smokey/sched-quota/Makefile \ diff --git a/include/rtdm/Makefile.am b/include/rtdm/Makefile.am index a8773ed..c837a05 100644 --- a/include/rtdm/Makefile.am +++ b/include/rtdm/Makefile.am @@ -7,6 +7,7 @@ includesub_HEADERS += \ analogy.h \ autotune.h \ can.h \ + gpio.h \ ipc.h \ serial.h \ testing.h \ diff --git a/include/rtdm/gpio.h b/include/rtdm/gpio.h new file mode 100644 index 0000000..c61f229 --- /dev/null +++ b/include/rtdm/gpio.h @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2016 Philippe Gerum <r...@xenomai.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ +#ifndef _RTDM_GPIO_H +#define _RTDM_GPIO_H + +#include <rtdm/rtdm.h> +#include <rtdm/uapi/gpio.h> + +#endif /* !_RTDM_GPIO_H */ diff --git a/include/rtdm/uapi/Makefile.am b/include/rtdm/uapi/Makefile.am index 9560310..c288ff9 100644 --- a/include/rtdm/uapi/Makefile.am +++ b/include/rtdm/uapi/Makefile.am @@ -7,6 +7,7 @@ includesub_HEADERS += \ analogy.h \ autotune.h \ can.h \ + gpio.h \ ipc.h \ serial.h \ testing.h \ diff --git a/include/rtdm/uapi/gpio.h b/include/rtdm/uapi/gpio.h new file mode 100644 index 0000000..b0d4899 --- /dev/null +++ b/include/rtdm/uapi/gpio.h @@ -0,0 +1,28 @@ +/** + * @note Copyright (C) 2016 Philippe Gerum <r...@xenomai.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#ifndef _RTDM_UAPI_GPIO_H +#define _RTDM_UAPI_GPIO_H + +#include <linux/types.h> + +#define GPIO_RTIOC_DIR_OUT _IOW(RTDM_CLASS_GPIO, 0, int) +#define GPIO_RTIOC_DIR_IN _IO(RTDM_CLASS_GPIO, 1) +#define GPIO_RTIOC_IRQEN _IO(RTDM_CLASS_GPIO, 2) +#define GPIO_RTIOC_IRQDIS _IO(RTDM_CLASS_GPIO, 3) + +#endif /* !_RTDM_UAPI_GPIO_H */ diff --git a/include/rtdm/uapi/rtdm.h b/include/rtdm/uapi/rtdm.h index eed3b36..c49378c 100644 --- a/include/rtdm/uapi/rtdm.h +++ b/include/rtdm/uapi/rtdm.h @@ -79,6 +79,7 @@ typedef int64_t nanosecs_rel_t; #define RTDM_CLASS_COBALT 8 #define RTDM_CLASS_UDD 9 #define RTDM_CLASS_MEMORY 10 +#define RTDM_CLASS_GPIO 11 /* #define RTDM_CLASS_USB ? #define RTDM_CLASS_FIREWIRE ? diff --git a/kernel/drivers/Kconfig b/kernel/drivers/Kconfig index a8b4c26..cbb6222 100644 --- a/kernel/drivers/Kconfig +++ b/kernel/drivers/Kconfig @@ -28,5 +28,6 @@ source "drivers/xenomai/net/Kconfig" source "drivers/xenomai/analogy/Kconfig" source "drivers/xenomai/ipc/Kconfig" source "drivers/xenomai/udd/Kconfig" +source "drivers/xenomai/gpio/Kconfig" endmenu diff --git a/kernel/drivers/Makefile b/kernel/drivers/Makefile index 4d80f68..c9cb567 100644 --- a/kernel/drivers/Makefile +++ b/kernel/drivers/Makefile @@ -1 +1 @@ -obj-$(CONFIG_XENOMAI) += autotune/ serial/ testing/ can/ net/ analogy/ ipc/ udd/ +obj-$(CONFIG_XENOMAI) += autotune/ serial/ testing/ can/ net/ analogy/ ipc/ udd/ gpio/ diff --git a/kernel/drivers/gpio/Kconfig b/kernel/drivers/gpio/Kconfig new file mode 100644 index 0000000..001c1bc --- /dev/null +++ b/kernel/drivers/gpio/Kconfig @@ -0,0 +1,25 @@ +menu "Real-time GPIO drivers" + +config XENO_DRIVERS_GPIO + bool + depends on GPIOLIB + +config XENO_DRIVERS_GPIO_BCM2708 + depends on MACH_BCM2708 + select XENO_DRIVERS_GPIO + bool "Support for BCM2708 GPIOs" + help + + Suitable for the GPIO controller present on Broadcom's BCM2708 + chip family. + +config XENO_DRIVERS_GPIO_MXC + depends on GPIO_MXC + select XENO_DRIVERS_GPIO + bool "Support for MXC GPIOs" + help + + Suitable for the GPIO controller available with + Freescale/NXP's MXC architecture. + +endmenu diff --git a/kernel/drivers/gpio/Makefile b/kernel/drivers/gpio/Makefile new file mode 100644 index 0000000..5d194a1 --- /dev/null +++ b/kernel/drivers/gpio/Makefile @@ -0,0 +1,7 @@ + +obj-$(CONFIG_XENO_DRIVERS_GPIO) += xeno_gpio.o + +xeno_gpio-y := gpio-core.o + +xeno_gpio-$(CONFIG_XENO_DRIVERS_GPIO_BCM2708) += gpio-bcm2708.o +xeno_gpio-$(CONFIG_XENO_DRIVERS_GPIO_MXC) += gpio-mxc.o diff --git a/kernel/drivers/gpio/gpio-bcm2708.c b/kernel/drivers/gpio/gpio-bcm2708.c new file mode 100644 index 0000000..2b5b6ac --- /dev/null +++ b/kernel/drivers/gpio/gpio-bcm2708.c @@ -0,0 +1,39 @@ +/** + * @note Copyright (C) 2016 Philippe Gerum <r...@xenomai.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#include <linux/module.h> +#include "gpio-core.h" + +#define RTDM_SUBCLASS_BCM2708 1 + +static struct rtdm_gpio_chip bcm2708_gpio_chip; + +static int __init bcm2708_gpio_init(void) +{ + return rtdm_gpiochip_add_by_name(&bcm2708_gpio_chip, "bcm2708_gpio", + RTDM_SUBCLASS_BCM2708); +} + +static void __exit bcm2708_gpio_exit(void) +{ + rtdm_gpiochip_remove(&bcm2708_gpio_chip); +} + +module_init(bcm2708_gpio_init); +module_exit(bcm2708_gpio_exit); + +MODULE_LICENSE("GPL"); diff --git a/kernel/drivers/gpio/gpio-core.c b/kernel/drivers/gpio/gpio-core.c new file mode 100644 index 0000000..4f707b8 --- /dev/null +++ b/kernel/drivers/gpio/gpio-core.c @@ -0,0 +1,349 @@ +/** + * @note Copyright (C) 2016 Philippe Gerum <r...@xenomai.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#include <linux/module.h> +#include <linux/init.h> +#include <linux/device.h> +#include <linux/gpio.h> +#include <linux/irq.h> +#include <linux/slab.h> +#include <linux/err.h> +#include <linux/of_platform.h> +#include "gpio-core.h" + +struct rtdm_gpio_pin { + struct rtdm_device dev; + struct list_head next; + rtdm_irq_t irqh; + rtdm_event_t event; + char *name; + struct gpio_desc *desc; +}; + +static int gpio_pin_interrupt(rtdm_irq_t *irqh) +{ + struct rtdm_gpio_pin *pin; + + pin = rtdm_irq_get_arg(irqh, struct rtdm_gpio_pin); + + rtdm_event_signal(&pin->event); + + return RTDM_IRQ_HANDLED; +} + +static int request_gpio_irq(unsigned int gpio, struct rtdm_gpio_pin *pin) +{ + unsigned int irq; + int ret; + + ret = gpio_request(gpio, pin->name); + if (ret) { + if (ret != -EPROBE_DEFER) + printk(XENO_ERR "cannot request GPIO%d\n", gpio); + return ret; + } + + ret = gpio_direction_input(gpio); + if (ret) { + printk(XENO_ERR "cannot set GPIO%d as input\n", gpio); + goto fail; + } + + gpio_export(gpio, true); + + rtdm_event_clear(&pin->event); + irq = gpio_to_irq(gpio); + ret = rtdm_irq_request(&pin->irqh, irq, gpio_pin_interrupt, + 0, pin->name, pin); + if (ret) { + printk(XENO_ERR "cannot request GPIO%d interrupt\n", gpio); + goto fail; + } + + rtdm_irq_enable(&pin->irqh); + + return 0; +fail: + gpio_free(gpio); + + return ret; +} + +static void release_gpio_irq(unsigned int gpio, struct rtdm_gpio_pin *pin) +{ + rtdm_irq_free(&pin->irqh); + gpio_free(gpio); +} + +static int gpio_pin_ioctl_nrt(struct rtdm_fd *fd, + unsigned int request, void *arg) +{ + struct rtdm_device *dev = rtdm_fd_device(fd); + unsigned int gpio = rtdm_fd_minor(fd); + struct rtdm_gpio_pin *pin; + int ret = 0, val; + + pin = container_of(dev, struct rtdm_gpio_pin, dev); + + switch (request) { + case GPIO_RTIOC_DIR_OUT: + ret = rtdm_safe_copy_from_user(fd, &val, arg, sizeof(val)); + if (ret) + return ret; + ret = gpio_direction_output(gpio, val); + break; + case GPIO_RTIOC_DIR_IN: + ret = gpio_direction_input(gpio); + break; + case GPIO_RTIOC_IRQEN: + ret = request_gpio_irq(gpio, pin); + break; + case GPIO_RTIOC_IRQDIS: + release_gpio_irq(gpio, pin); + break; + default: + return -EINVAL; + } + + return ret; +} + +static ssize_t gpio_pin_read_rt(struct rtdm_fd *fd, + void __user *buf, size_t len) +{ + struct rtdm_device *dev = rtdm_fd_device(fd); + struct rtdm_gpio_pin *pin; + int value, ret; + + if (len < sizeof(value)) + return -EINVAL; + + pin = container_of(dev, struct rtdm_gpio_pin, dev); + value = gpiod_get_raw_value(pin->desc); + ret = rtdm_safe_copy_to_user(fd, buf, &value, sizeof(value)); + + return ret ?: sizeof(value); +} + +static ssize_t gpio_pin_write_rt(struct rtdm_fd *fd, + const void __user *buf, size_t len) +{ + struct rtdm_device *dev = rtdm_fd_device(fd); + struct rtdm_gpio_pin *pin; + int value, ret; + + if (len < sizeof(value)) + return -EINVAL; + + ret = rtdm_safe_copy_from_user(fd, &value, buf, sizeof(value)); + if (ret) + return ret; + + pin = container_of(dev, struct rtdm_gpio_pin, dev); + gpiod_set_raw_value(pin->desc, value); + + return sizeof(value); +} + +static int gpio_pin_select(struct rtdm_fd *fd, struct xnselector *selector, + unsigned int type, unsigned int index) +{ + struct rtdm_device *dev = rtdm_fd_device(fd); + struct rtdm_gpio_pin *pin; + + pin = container_of(dev, struct rtdm_gpio_pin, dev); + + return rtdm_event_select(&pin->event, selector, type, index); +} + +static void delete_pin_devices(struct rtdm_gpio_chip *rgc) +{ + struct rtdm_gpio_pin *pin, *n; + struct rtdm_device *dev; + + list_for_each_entry_safe(pin, n, &rgc->pins, next) { + list_del(&pin->next); + dev = &pin->dev; + rtdm_dev_unregister(dev); + rtdm_event_destroy(&pin->event); + kfree(dev->label); + kfree(pin->name); + kfree(pin); + } +} + +static int create_pin_devices(struct rtdm_gpio_chip *rgc) +{ + struct gpio_chip *gc = rgc->gc; + struct rtdm_gpio_pin *pin; + struct rtdm_device *dev; + int n, ret; + + for (n = gc->base; n < gc->base + gc->ngpio - 1; n++) { + ret = -ENOMEM; + pin = kzalloc(sizeof(*pin), GFP_KERNEL); + if (pin == NULL) + goto fail; + pin->name = kasprintf(GFP_KERNEL, "gpio%d", n); + if (pin->name == NULL) + goto fail_name; + pin->desc = gpio_to_desc(n); + if (pin->desc == NULL) { + ret = -ENODEV; + goto fail_desc; + } + dev = &pin->dev; + dev->driver = &rgc->driver; + dev->label = kasprintf(GFP_KERNEL, "%s/gpio%%d", gc->label); + if (dev->label == NULL) + goto fail_label; + dev->minor = n; + dev->device_data = rgc; + ret = rtdm_dev_register(dev); + if (ret) + goto fail_register; + rtdm_event_init(&pin->event, 0); + list_add_tail(&pin->next, &rgc->pins); + } + + return 0; + +fail_register: + kfree(dev->label); +fail_desc: +fail_label: + kfree(pin->name); +fail_name: + kfree(pin); +fail: + delete_pin_devices(rgc); + + return ret; +} + +static char *gpio_pin_devnode(struct device *dev, umode_t *mode) +{ + return kasprintf(GFP_KERNEL, "rtdm/%s/%s", + dev->class->name, + dev_name(dev)); +} + +int rtdm_gpiochip_add(struct rtdm_gpio_chip *rgc, + struct gpio_chip *gc, int gpio_subclass) +{ + int ret; + + if (!realtime_core_enabled()) + return 0; + + rgc->devclass = class_create(gc->owner, gc->label); + if (IS_ERR(rgc->devclass)) { + printk(XENO_ERR "cannot create sysfs class\n"); + return PTR_ERR(rgc->devclass); + } + rgc->devclass->devnode = gpio_pin_devnode; + + rgc->driver.profile_info = (struct rtdm_profile_info) + RTDM_PROFILE_INFO(rtdm_gpio_chip, + RTDM_CLASS_GPIO, + gpio_subclass, + 0); + rgc->driver.device_flags = RTDM_NAMED_DEVICE|RTDM_FIXED_MINOR; + rgc->driver.base_minor = gc->base; + rgc->driver.device_count = gc->ngpio; + rgc->driver.context_size = 0; + rgc->driver.ops = (struct rtdm_fd_ops){ + .ioctl_nrt = gpio_pin_ioctl_nrt, + .read_rt = gpio_pin_read_rt, + .write_rt = gpio_pin_write_rt, + .select = gpio_pin_select, + }; + + rtdm_drv_set_sysclass(&rgc->driver, rgc->devclass); + + rgc->gc = gc; + INIT_LIST_HEAD(&rgc->pins); + + ret = create_pin_devices(rgc); + if (ret) + class_destroy(rgc->devclass); + + return ret; +} +EXPORT_SYMBOL_GPL(rtdm_gpiochip_add); + +void rtdm_gpiochip_remove(struct rtdm_gpio_chip *rgc) +{ + if (!realtime_core_enabled()) + return; + + delete_pin_devices(rgc); + class_destroy(rgc->devclass); +} +EXPORT_SYMBOL_GPL(rtdm_gpiochip_remove); + +static int gpiochip_match_name(struct gpio_chip *chip, void *data) +{ + const char *name = data; + + return !strcmp(chip->label, name); +} + +static struct gpio_chip *find_chip_by_name(const char *name) +{ + return gpiochip_find((void *)name, gpiochip_match_name); +} + +int rtdm_gpiochip_add_by_name(struct rtdm_gpio_chip *rgc, + const char *label, int gpio_subclass) +{ + struct gpio_chip *gc = find_chip_by_name(label); + + if (gc == NULL) + return -EPROBE_DEFER; + + return rtdm_gpiochip_add(rgc, gc, gpio_subclass); +} +EXPORT_SYMBOL_GPL(rtdm_gpiochip_add_by_name); + +int rtdm_gpiochip_scan_of(struct device_node *from, const char *compat, + int (*match)(struct gpio_chip *gc)) +{ + struct device_node *np = from; + struct platform_device *pdev; + int ret = -EPROBE_DEFER; + struct gpio_chip *gc; + + for (;;) { + np = of_find_compatible_node(np, NULL, compat); + if (np == NULL) + break; + pdev = of_find_device_by_node(np); + of_node_put(np); + if (pdev == NULL) + return -ENODEV; + gc = find_chip_by_name(dev_name(&pdev->dev)); + if (gc) { + ret = match(gc); + if (ret) + break; + } + } + + return ret; +} +EXPORT_SYMBOL_GPL(rtdm_gpiochip_scan_of); diff --git a/kernel/drivers/gpio/gpio-core.h b/kernel/drivers/gpio/gpio-core.h new file mode 100644 index 0000000..3fa7867 --- /dev/null +++ b/kernel/drivers/gpio/gpio-core.h @@ -0,0 +1,48 @@ +/** + * @note Copyright (C) 2016 Philippe Gerum <r...@xenomai.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#ifndef _RTDM_GPIO_CORE_H +#define _RTDM_GPIO_CORE_H + +#include <linux/list.h> +#include <rtdm/driver.h> +#include <rtdm/uapi/gpio.h> + +struct class; +struct device_node; + +struct rtdm_gpio_chip { + struct gpio_chip *gc; + struct rtdm_driver driver; + struct class *devclass; + struct list_head pins; + struct list_head next; +}; + +int rtdm_gpiochip_add(struct rtdm_gpio_chip *rgc, + struct gpio_chip *gc, + int gpio_subclass); + +void rtdm_gpiochip_remove(struct rtdm_gpio_chip *rgc); + +int rtdm_gpiochip_add_by_name(struct rtdm_gpio_chip *rgc, + const char *label, int gpio_subclass); + +int rtdm_gpiochip_scan_of(struct device_node *from, const char *compat, + int (*match)(struct gpio_chip *gc)); + +#endif /* !_RTDM_GPIO_CORE_H */ diff --git a/kernel/drivers/gpio/gpio-mxc.c b/kernel/drivers/gpio/gpio-mxc.c new file mode 100644 index 0000000..808cd54 --- /dev/null +++ b/kernel/drivers/gpio/gpio-mxc.c @@ -0,0 +1,67 @@ +/** + * @note Copyright (C) 2016 Philippe Gerum <r...@xenomai.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#include <linux/module.h> +#include <linux/list.h> +#include <linux/gpio.h> +#include <linux/slab.h> +#include "gpio-core.h" + +#define RTDM_SUBCLASS_MXC 2 + +static LIST_HEAD(mxc_gpio_chips); + +static int match_gpio_chip(struct gpio_chip *gc) +{ + struct rtdm_gpio_chip *rgc; + int ret; + + rgc = kzalloc(sizeof(*rgc), GFP_KERNEL); + if (rgc == NULL) + return -ENOMEM; + + ret = rtdm_gpiochip_add(rgc, gc, RTDM_SUBCLASS_MXC); + if (ret) { + kfree(rgc); + return ret; + } + + list_add(&rgc->next, &mxc_gpio_chips); + + return 0; +} + +static int __init mxc_gpio_init(void) +{ + return rtdm_gpiochip_scan_of(NULL, "fsl,imx6q-gpio", match_gpio_chip); +} + +static void __exit mxc_gpio_exit(void) +{ + struct rtdm_gpio_chip *rgc, *n; + + list_for_each_entry_safe(rgc, n, &mxc_gpio_chips, next) { + list_del(&rgc->next); + rtdm_gpiochip_remove(rgc); + kfree(rgc); + } +} + +module_init(mxc_gpio_init); +module_exit(mxc_gpio_exit); + +MODULE_LICENSE("GPL"); _______________________________________________ Xenomai-git mailing list Xenomai-git@xenomai.org https://xenomai.org/mailman/listinfo/xenomai-git