/** Network Driver for Beckhoff CCAT communication controller Copyright (C) 2014 Beckhoff Automation GmbH Author: Patrick Bruenn 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 #include #include #include #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, };