--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/devices/ccat/gpio.c Tue Feb 16 15:18:34 2016 +0100
@@ -0,0 +1,164 @@
+/**
+ Network Driver for Beckhoff CCAT communication controller
+ Copyright (C) 2014 Beckhoff Automation GmbH
+ Author: Patrick Bruenn <p.bruenn@beckhoff.com>
+
+ 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.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/gpio.h>
+#include <linux/version.h>
+#include "module.h"
+
+/**
+ * struct ccat_gpio - CCAT GPIO function
+ * @ioaddr: PCI base address of the CCAT Update function
+ * @info: holds a copy of the CCAT Update function information block (read from PCI config space)
+ */
+struct ccat_gpio {
+ struct gpio_chip chip;
+ void __iomem *ioaddr;
+ struct mutex lock;
+};
+
+/** TODO implement in LED driver
+ #define TC_RED 0x01
+ #define TC_GREEN 0x02
+ #define TC_BLUE 0x04
+ #define FB1_RED 0x08
+ #define FB1_GREEN 0x10
+ #define FB1_BLUE 0x20
+ #define FB2_RED 0x40
+ #define FB2_GREEN 0x80
+ #define FB2_BLUE 0x100
+ */
+
+static int set_bit_in_register(struct mutex *lock, void __iomem * ioaddr,
+ unsigned nr, int val)
+{
+ volatile unsigned long old;
+
+ mutex_lock(lock);
+ old = ioread32(ioaddr);
+ val ? set_bit(nr, &old) : clear_bit(nr, &old);
+ if (val)
+ set_bit(nr, &old);
+ else
+ clear_bit(nr, &old);
+ iowrite32(old, ioaddr);
+ mutex_unlock(lock);
+ return 0;
+}
+
+static int ccat_gpio_get_direction(struct gpio_chip *chip, unsigned nr)
+{
+ struct ccat_gpio *gdev = container_of(chip, struct ccat_gpio, chip);
+ const size_t byte_offset = 4 * (nr / 32) + 0x8;
+ const u32 mask = 1 << (nr % 32);
+
+ return !(mask & ioread32(gdev->ioaddr + byte_offset));
+}
+
+static int ccat_gpio_direction_input(struct gpio_chip *chip, unsigned nr)
+{
+ struct ccat_gpio *gdev = container_of(chip, struct ccat_gpio, chip);
+
+ return set_bit_in_register(&gdev->lock, gdev->ioaddr + 0x8, nr, 0);
+}
+
+static int ccat_gpio_direction_output(struct gpio_chip *chip, unsigned nr,
+ int val)
+{
+ struct ccat_gpio *gdev = container_of(chip, struct ccat_gpio, chip);
+
+ return set_bit_in_register(&gdev->lock, gdev->ioaddr + 0x8, nr, 1);
+}
+
+static int ccat_gpio_get(struct gpio_chip *chip, unsigned nr)
+{
+ struct ccat_gpio *gdev = container_of(chip, struct ccat_gpio, chip);
+ const size_t byte_off = 4 * (nr / 32);
+ const int mask = 1 << (nr % 32);
+ int dir_off;
+ int value;
+
+ /** omit direction changes before value was read */
+ mutex_lock(&gdev->lock);
+ dir_off = 0x10 * ccat_gpio_get_direction(chip, nr);
+ value = !(mask & ioread32(gdev->ioaddr + byte_off + dir_off));
+ mutex_unlock(&gdev->lock);
+ return value;
+}
+
+static void ccat_gpio_set(struct gpio_chip *chip, unsigned nr, int val)
+{
+ struct ccat_gpio *gdev = container_of(chip, struct ccat_gpio, chip);
+
+ set_bit_in_register(&gdev->lock, gdev->ioaddr, nr, val);
+}
+
+static const struct gpio_chip ccat_gpio_chip = {
+ .label = KBUILD_MODNAME,
+ .owner = THIS_MODULE,
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,12,0))
+ .get_direction = ccat_gpio_get_direction,
+#endif
+ .direction_input = ccat_gpio_direction_input,
+ .get = ccat_gpio_get,
+ .direction_output = ccat_gpio_direction_output,
+ .set = ccat_gpio_set,
+ .dbg_show = NULL,
+ .base = -1,
+ .can_sleep = false
+};
+
+static int ccat_gpio_probe(struct ccat_function *func)
+{
+ struct ccat_gpio *const gpio = kzalloc(sizeof(*gpio), GFP_KERNEL);
+ int ret;
+
+ if (!gpio)
+ return -ENOMEM;
+
+ gpio->ioaddr = func->ccat->bar_0 + func->info.addr;
+ memcpy(&gpio->chip, &ccat_gpio_chip, sizeof(gpio->chip));
+ gpio->chip.ngpio = func->info.num_gpios;
+ mutex_init(&gpio->lock);
+
+ ret = gpiochip_add(&gpio->chip);
+ if (ret) {
+ kfree(gpio);
+ return ret;
+ }
+ pr_info("registered %s as gpiochip%d with #%d GPIOs.\n",
+ gpio->chip.label, gpio->chip.base, gpio->chip.ngpio);
+ func->private_data = gpio;
+ return 0;
+}
+
+static void ccat_gpio_remove(struct ccat_function *func)
+{
+ struct ccat_gpio *const gpio = func->private_data;
+
+ gpiochip_remove(&gpio->chip);
+};
+
+const struct ccat_driver gpio_driver = {
+ .type = CCATINFO_GPIO,
+ .probe = ccat_gpio_probe,
+ .remove = ccat_gpio_remove,
+};