p@2636: /**
p@2636: Network Driver for Beckhoff CCAT communication controller
p@2636: Copyright (C) 2014 Beckhoff Automation GmbH
p@2636: Author: Patrick Bruenn
p@2636:
p@2636: This program is free software; you can redistribute it and/or modify
p@2636: it under the terms of the GNU General Public License as published by
p@2636: the Free Software Foundation; either version 2 of the License, or
p@2636: (at your option) any later version.
p@2636:
p@2636: This program is distributed in the hope that it will be useful,
p@2636: but WITHOUT ANY WARRANTY; without even the implied warranty of
p@2636: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
p@2636: GNU General Public License for more details.
p@2636:
p@2636: You should have received a copy of the GNU General Public License along
p@2636: with this program; if not, write to the Free Software Foundation, Inc.,
p@2636: 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
p@2636: */
p@2636:
p@2636: #include
p@2636: #include
p@2636: #include
p@2636: #include
p@2636: #include "module.h"
p@2636:
p@2636: /**
p@2636: * struct ccat_gpio - CCAT GPIO function
p@2636: * @ioaddr: PCI base address of the CCAT Update function
p@2636: * @info: holds a copy of the CCAT Update function information block (read from PCI config space)
p@2636: */
p@2636: struct ccat_gpio {
p@2636: struct gpio_chip chip;
p@2636: void __iomem *ioaddr;
p@2636: struct mutex lock;
p@2636: };
p@2636:
p@2636: /** TODO implement in LED driver
p@2636: #define TC_RED 0x01
p@2636: #define TC_GREEN 0x02
p@2636: #define TC_BLUE 0x04
p@2636: #define FB1_RED 0x08
p@2636: #define FB1_GREEN 0x10
p@2636: #define FB1_BLUE 0x20
p@2636: #define FB2_RED 0x40
p@2636: #define FB2_GREEN 0x80
p@2636: #define FB2_BLUE 0x100
p@2636: */
p@2636:
p@2636: static int set_bit_in_register(struct mutex *lock, void __iomem * ioaddr,
p@2636: unsigned nr, int val)
p@2636: {
p@2636: volatile unsigned long old;
p@2636:
p@2636: mutex_lock(lock);
p@2636: old = ioread32(ioaddr);
p@2636: val ? set_bit(nr, &old) : clear_bit(nr, &old);
p@2636: if (val)
p@2636: set_bit(nr, &old);
p@2636: else
p@2636: clear_bit(nr, &old);
p@2636: iowrite32(old, ioaddr);
p@2636: mutex_unlock(lock);
p@2636: return 0;
p@2636: }
p@2636:
p@2636: static int ccat_gpio_get_direction(struct gpio_chip *chip, unsigned nr)
p@2636: {
p@2636: struct ccat_gpio *gdev = container_of(chip, struct ccat_gpio, chip);
p@2636: const size_t byte_offset = 4 * (nr / 32) + 0x8;
p@2636: const u32 mask = 1 << (nr % 32);
p@2636:
p@2636: return !(mask & ioread32(gdev->ioaddr + byte_offset));
p@2636: }
p@2636:
p@2636: static int ccat_gpio_direction_input(struct gpio_chip *chip, unsigned nr)
p@2636: {
p@2636: struct ccat_gpio *gdev = container_of(chip, struct ccat_gpio, chip);
p@2636:
p@2636: return set_bit_in_register(&gdev->lock, gdev->ioaddr + 0x8, nr, 0);
p@2636: }
p@2636:
p@2636: static int ccat_gpio_direction_output(struct gpio_chip *chip, unsigned nr,
p@2636: int val)
p@2636: {
p@2636: struct ccat_gpio *gdev = container_of(chip, struct ccat_gpio, chip);
p@2636:
p@2636: return set_bit_in_register(&gdev->lock, gdev->ioaddr + 0x8, nr, 1);
p@2636: }
p@2636:
p@2636: static int ccat_gpio_get(struct gpio_chip *chip, unsigned nr)
p@2636: {
p@2636: struct ccat_gpio *gdev = container_of(chip, struct ccat_gpio, chip);
p@2636: const size_t byte_off = 4 * (nr / 32);
p@2636: const int mask = 1 << (nr % 32);
p@2636: int dir_off;
p@2636: int value;
p@2636:
p@2636: /** omit direction changes before value was read */
p@2636: mutex_lock(&gdev->lock);
p@2636: dir_off = 0x10 * ccat_gpio_get_direction(chip, nr);
p@2636: value = !(mask & ioread32(gdev->ioaddr + byte_off + dir_off));
p@2636: mutex_unlock(&gdev->lock);
p@2636: return value;
p@2636: }
p@2636:
p@2636: static void ccat_gpio_set(struct gpio_chip *chip, unsigned nr, int val)
p@2636: {
p@2636: struct ccat_gpio *gdev = container_of(chip, struct ccat_gpio, chip);
p@2636:
p@2636: set_bit_in_register(&gdev->lock, gdev->ioaddr, nr, val);
p@2636: }
p@2636:
p@2636: static const struct gpio_chip ccat_gpio_chip = {
p@2636: .label = KBUILD_MODNAME,
p@2636: .owner = THIS_MODULE,
p@2636: #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,12,0))
p@2636: .get_direction = ccat_gpio_get_direction,
p@2636: #endif
p@2636: .direction_input = ccat_gpio_direction_input,
p@2636: .get = ccat_gpio_get,
p@2636: .direction_output = ccat_gpio_direction_output,
p@2636: .set = ccat_gpio_set,
p@2636: .dbg_show = NULL,
p@2636: .base = -1,
p@2636: .can_sleep = false
p@2636: };
p@2636:
p@2636: static int ccat_gpio_probe(struct ccat_function *func)
p@2636: {
p@2636: struct ccat_gpio *const gpio = kzalloc(sizeof(*gpio), GFP_KERNEL);
p@2636: int ret;
p@2636:
p@2636: if (!gpio)
p@2636: return -ENOMEM;
p@2636:
p@2636: gpio->ioaddr = func->ccat->bar_0 + func->info.addr;
p@2636: memcpy(&gpio->chip, &ccat_gpio_chip, sizeof(gpio->chip));
p@2636: gpio->chip.ngpio = func->info.num_gpios;
p@2636: mutex_init(&gpio->lock);
p@2636:
p@2636: ret = gpiochip_add(&gpio->chip);
p@2636: if (ret) {
p@2636: kfree(gpio);
p@2636: return ret;
p@2636: }
p@2638: pr_info("registered %s as gpiochip%d with #%d GPIOs.\n",
p@2638: gpio->chip.label, gpio->chip.base, gpio->chip.ngpio);
p@2636: func->private_data = gpio;
p@2636: return 0;
p@2636: }
p@2636:
p@2636: static void ccat_gpio_remove(struct ccat_function *func)
p@2636: {
p@2636: struct ccat_gpio *const gpio = func->private_data;
p@2636:
p@2636: gpiochip_remove(&gpio->chip);
p@2636: };
p@2636:
p@2638: const struct ccat_driver gpio_driver = {
p@2636: .type = CCATINFO_GPIO,
p@2636: .probe = ccat_gpio_probe,
p@2636: .remove = ccat_gpio_remove,
p@2636: };