From: Kevin Strasser <kevin.strasser@linux.intel.com> To: linux-kernel@vger.kernel.org Cc: Michael Brunner <michael.brunner@kontron.com>, Samuel Ortiz <sameo@linux.intel.com>, Wolfram Sang <wsa@the-dreams.de>, Ben Dooks <ben-linux@fluff.org>, linux-i2c@vger.kernel.org, Grant Likely <grant.likely@secretlab.ca>, Linus Walleij <linus.walleij@linaro.org>, Wim Van Sebroeck <wim@iguana.be>, linux-watchdog@vger.kernel.org, Darren Hart <dvhart@linux.intel.com>, Michael Brunner <mibru@gmx.de>, Greg Kroah-Hartman <gregkh@linuxfoundation.org>, Kevin Strasser <kevin.strasser@linux.intel.com> Subject: [PATCH 3/4] gpio: Kontron PLD gpio driver Date: Mon, 8 Apr 2013 10:15:20 -0700 [thread overview] Message-ID: <1365441321-21952-3-git-send-email-kevin.strasser@linux.intel.com> (raw) In-Reply-To: <1365441321-21952-1-git-send-email-kevin.strasser@linux.intel.com> From: Michael Brunner <michael.brunner@kontron.com> Add gpio support for the on-board PLD found on some Kontron embedded modules. Signed-off-by: Michael Brunner <michael.brunner@kontron.com> Signed-off-by: Kevin Strasser <kevin.strasser@linux.intel.com> --- drivers/gpio/Kconfig | 22 ++ drivers/gpio/Makefile | 2 + drivers/gpio/gpio-kempld.c | 476 +++++++++++++++++++++++++++++++++++++++ drivers/gpio/gpio-kempld.h | 50 ++++ drivers/gpio/gpio-kempld_now1.c | 280 +++++++++++++++++++++++ 5 files changed, 830 insertions(+) create mode 100644 drivers/gpio/gpio-kempld.c create mode 100644 drivers/gpio/gpio-kempld.h create mode 100644 drivers/gpio/gpio-kempld_now1.c diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 93aaadf..88e1bc9 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -658,6 +658,28 @@ config GPIO_UCB1400 This enables support for the Philips UCB1400 GPIO pins. The UCB1400 is an AC97 audio codec. +comment "LPC GPIO expanders:" + +config GPIO_KEMPLD + tristate "Kontron COM GPIO" + depends on MFD_KEMPLD + help + This enables support for the PLD GPIO interface on some Kontron ETX + and COMexpress (ETXexpress) modules. + + This driver can also be built as a module. If so, the module will + be called gpio-kempld. + +config GPIO_NOW1_KEMPLD + tristate "Kontron COMe-mSP1 (nanoETXexpress-SP) GPIO" + depends on MFD_KEMPLD + help + This enables support for the PLD GPIO interface on the Kontron + COMe-mSP1 (nanoETXexpress-SP) module. + + This driver can also be built as a module. If so, the module will + be called gpio-kempld_now1. + comment "MODULbus GPIO expanders:" config GPIO_JANZ_TTL diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 22e07bc..59919db 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -28,6 +28,8 @@ obj-$(CONFIG_GPIO_GE_FPGA) += gpio-ge.o obj-$(CONFIG_GPIO_ICH) += gpio-ich.o obj-$(CONFIG_GPIO_IT8761E) += gpio-it8761e.o obj-$(CONFIG_GPIO_JANZ_TTL) += gpio-janz-ttl.o +obj-$(CONFIG_GPIO_KEMPLD) += gpio-kempld.o +obj-$(CONFIG_GPIO_NOW1_KEMPLD) += gpio-kempld_now1.o obj-$(CONFIG_ARCH_KS8695) += gpio-ks8695.o obj-$(CONFIG_GPIO_LANGWELL) += gpio-langwell.o obj-$(CONFIG_ARCH_LPC32XX) += gpio-lpc32xx.o diff --git a/drivers/gpio/gpio-kempld.c b/drivers/gpio/gpio-kempld.c new file mode 100644 index 0000000..e18967d --- /dev/null +++ b/drivers/gpio/gpio-kempld.c @@ -0,0 +1,476 @@ +/* + * kempld_gpio.c - Kontron PLD GPIO driver + * + * Copyright (c) 2010-2012 Kontron Europe GmbH + * Author: Michael Brunner <michael.brunner@kontron.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License 2 as published + * by the Free Software Foundation. + * + * 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; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/errno.h> +#include <linux/acpi.h> +#include <linux/platform_device.h> +#include <linux/gpio.h> +#include <linux/mfd/kempld.h> +#include <linux/seq_file.h> + +#include "gpio-kempld.h" + +static int gpiobase = -1; +static int gpioien = 0x00; +static int gpioevt_lvl_edge = -1; +static int gpioevt_low_high = -1; +static int gpionmien = 0x00; + +static int kempld_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + struct kempld_gpio_data *gpio + = container_of(chip, struct kempld_gpio_data, chip); + struct kempld_device_data *pld = gpio->pld; + int status; + + pld = gpio->pld; + + kempld_get_mutex_set_index(pld, KEMPLD_GPIO_LVL_NUM(offset)); + + status = kempld_read8(pld, KEMPLD_GPIO_LVL_NUM(offset)); + status &= KEMPLD_GPIO_MASK(offset); + + kempld_release_mutex(pld); + + return status; +} + +static void kempld_gpio_set(struct gpio_chip *chip, unsigned offset, + int value) +{ + struct kempld_gpio_data *gpio + = container_of(chip, struct kempld_gpio_data, chip); + struct kempld_device_data *pld = gpio->pld; + int status; + + + kempld_get_mutex_set_index(pld, KEMPLD_GPIO_LVL_NUM(offset)); + + status = kempld_read8(pld, KEMPLD_GPIO_LVL_NUM(offset)); + if (value) + status |= KEMPLD_GPIO_MASK(offset); + else + status &= ~KEMPLD_GPIO_MASK(offset); + kempld_write8(pld, KEMPLD_GPIO_LVL_NUM(offset), status); + + kempld_release_mutex(pld); +} + +static int kempld_gpio_direction_input(struct gpio_chip *chip, unsigned offset) +{ + struct kempld_gpio_data *gpio + = container_of(chip, struct kempld_gpio_data, chip); + struct kempld_device_data *pld = gpio->pld; + int status; + + kempld_get_mutex_set_index(pld, KEMPLD_GPIO_DIR_NUM(offset)); + + status = kempld_read8(pld, KEMPLD_GPIO_DIR_NUM(offset)); + status &= ~KEMPLD_GPIO_MASK(offset); + kempld_write8(pld, KEMPLD_GPIO_DIR_NUM(offset), status); + + kempld_release_mutex(pld); + + + return 0; +} + +static int kempld_gpio_direction_output(struct gpio_chip *chip, + unsigned offset, int value) +{ + struct kempld_gpio_data *gpio + = container_of(chip, struct kempld_gpio_data, chip); + struct kempld_device_data *pld = gpio->pld; + int status; + + kempld_get_mutex_set_index(pld, KEMPLD_GPIO_LVL_NUM(offset)); + + status = kempld_read8(pld, KEMPLD_GPIO_LVL_NUM(offset)); + if (value) + status |= KEMPLD_GPIO_MASK(offset); + else + status &= ~KEMPLD_GPIO_MASK(offset); + kempld_write8(pld, KEMPLD_GPIO_LVL_NUM(offset), status); + + status = kempld_read8(pld, KEMPLD_GPIO_DIR_NUM(offset)); + status |= KEMPLD_GPIO_MASK(offset); + kempld_write8(pld, KEMPLD_GPIO_DIR_NUM(offset), status); + + kempld_release_mutex(pld); + + return 0; +} + +static int kempld_gpio_to_irq(struct gpio_chip *chip, unsigned offset) +{ + struct kempld_gpio_data *gpio + = container_of(chip, struct kempld_gpio_data, chip); + return gpio->irq; +} + +#ifdef CONFIG_DEBUG_FS +static int kempld_gpio_get_direction(struct gpio_chip *chip, unsigned offset) +{ + struct kempld_gpio_data *gpio + = container_of(chip, struct kempld_gpio_data, chip); + struct kempld_device_data *pld = gpio->pld; + int status; + + kempld_get_mutex_set_index(pld, KEMPLD_GPIO_DIR_NUM(offset)); + + status = kempld_read8(pld, KEMPLD_GPIO_DIR_NUM(offset)); + status &= KEMPLD_GPIO_MASK(offset); + + kempld_release_mutex(pld); + + + return status ? 1 : 0; +} + +static int kempld_gpio_get_ien(struct gpio_chip *chip, unsigned offset) +{ + struct kempld_gpio_data *gpio + = container_of(chip, struct kempld_gpio_data, chip); + struct kempld_device_data *pld = gpio->pld; + int status; + + kempld_get_mutex_set_index(pld, KEMPLD_GPIO_IEN_NUM(offset)); + + status = kempld_read8(pld, KEMPLD_GPIO_IEN_NUM(offset)); + status &= KEMPLD_GPIO_MASK(offset); + + kempld_release_mutex(pld); + + + return status ? 1 : 0; +} + +static int kempld_gpio_get_evt_lvl_edge(struct gpio_chip *chip, + unsigned offset) +{ + struct kempld_gpio_data *gpio + = container_of(chip, struct kempld_gpio_data, chip); + struct kempld_device_data *pld = gpio->pld; + int status; + + kempld_get_mutex_set_index(pld, KEMPLD_GPIO_EVT_LVL_EDGE_NUM(offset)); + + status = kempld_read8(pld, KEMPLD_GPIO_EVT_LVL_EDGE_NUM(offset)); + status &= KEMPLD_GPIO_MASK(offset); + + kempld_release_mutex(pld); + + + return status ? 1 : 0; +} + +static int kempld_gpio_get_evt_high_low(struct gpio_chip *chip, + unsigned offset) +{ + struct kempld_gpio_data *gpio + = container_of(chip, struct kempld_gpio_data, chip); + struct kempld_device_data *pld = gpio->pld; + int status; + + gpio = dev_get_drvdata(chip->dev); + pld = gpio->pld; + + kempld_get_mutex_set_index(pld, KEMPLD_GPIO_EVT_LOW_HIGH_NUM(offset)); + + status = kempld_read8(pld, KEMPLD_GPIO_EVT_LOW_HIGH_NUM(offset)); + status &= KEMPLD_GPIO_MASK(offset); + + kempld_release_mutex(pld); + + + return status ? 1 : 0; +} + +static int kempld_gpio_get_nmien(struct gpio_chip *chip, unsigned offset) +{ + struct kempld_gpio_data *gpio + = container_of(chip, struct kempld_gpio_data, chip); + struct kempld_device_data *pld = gpio->pld; + int status; + + gpio = dev_get_drvdata(chip->dev); + pld = gpio->pld; + + kempld_get_mutex_set_index(pld, KEMPLD_GPIO_NMIEN_NUM(offset)); + + status = kempld_read8(pld, KEMPLD_GPIO_NMIEN_NUM(offset)); + status &= KEMPLD_GPIO_MASK(offset); + + kempld_release_mutex(pld); + + + return status ? 1 : 0; +} + +static void kempld_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) +{ + int i; + + for (i = 0; i < chip->ngpio; i++) { + int gpio = i + chip->base; + + seq_printf(s, " gpio-%-3d %s %s", gpio, + kempld_gpio_get_direction(chip, i) + ? "out" : "in", + kempld_gpio_get(chip, i) + ? "hi" : "lo"); + seq_printf(s, ", event on %s (irq %s, nmi %s)\n", + (kempld_gpio_get_evt_lvl_edge(chip, i) + ? (kempld_gpio_get_evt_high_low(chip, i) + ? "rising edge" : "falling edge") : + (kempld_gpio_get_evt_high_low(chip, i) + ? "high level" : "low level")), + kempld_gpio_get_ien(chip, i) + ? "enabled" : "disabled", + kempld_gpio_get_nmien(chip, i) + ? "enabled" : "disabled"); + } +} +#else +#define kempld_gpio_dbg_show NULL +#endif + +static int kempld_gpio_setup_event(struct kempld_gpio_data *gpio) +{ + struct kempld_device_data *pld = gpio->pld; + struct gpio_chip *chip = &gpio->chip; + int irq; + + irq = gpio->irq; + + kempld_get_mutex_set_index(pld, KEMPLD_IRQ_GPIO); + irq = kempld_read8(pld, KEMPLD_IRQ_GPIO); + + /* Leave if interrupts are not supported by the GPIO core */ + if ((irq & 0xf0) == 0xf0) + return 0; + + gpio->irq = irq & 0x0f; + + if (gpioien & !gpio->mask) { + dev_err(chip->dev, "gpioien parameter is invalid"); + gpio->irq = 0; + } + + if (gpionmien & !gpio->mask) { + dev_err(chip->dev, "gpionmien parameter is invalid"); + gpio->irq = 0; + } + + if ((gpioevt_lvl_edge & !gpio->mask) && (gpioevt_lvl_edge != -1)) { + dev_err(chip->dev, "gpioevt_lvl_edge parameter is invalid"); + gpio->irq = 0; + } + + if ((gpioevt_low_high & !gpio->mask) && (gpioevt_low_high != -1)) { + dev_err(chip->dev, "gpioevt_low_high parameter is invalid"); + gpio->irq = 0; + } + + if (!gpio->irq) + return -EIO; + + if (gpioevt_lvl_edge != -1) + kempld_write8(pld, KEMPLD_GPIO_EVT_LVL_EDGE, gpioevt_lvl_edge); + + if (gpioevt_low_high != -1) + kempld_write8(pld, KEMPLD_GPIO_EVT_LOW_HIGH, gpioevt_low_high); + + kempld_write8(pld, KEMPLD_GPIO_NMIEN, gpionmien); + kempld_write8(pld, KEMPLD_GPIO_IEN, gpioien); + + kempld_release_mutex(pld); + + return 0; +} + + +static int kempld_gpio_detect(struct kempld_gpio_data *gpio) +{ + struct kempld_device_data *pld = gpio->pld; + struct gpio_chip *chip = &gpio->chip; + u16 evt, evt_back; + int i; + + kempld_get_mutex_set_index(pld, KEMPLD_GPIO_IEN); + + /* Backup event register as it might be already initialized */ + evt_back = kempld_read16(pld, KEMPLD_GPIO_EVT_LVL_EDGE); + + /* Disable interrupt enables and set event register to zero */ + kempld_write16(pld, KEMPLD_GPIO_IEN, 0x0000); + kempld_write16(pld, KEMPLD_GPIO_NMIEN, 0x0000); + kempld_write16(pld, KEMPLD_GPIO_EVT_LVL_EDGE, 0x0000); + + /* Read back event register */ + evt = kempld_read16(pld, KEMPLD_GPIO_EVT_LVL_EDGE); + + /* Restore event register */ + kempld_write16(pld, KEMPLD_GPIO_EVT_LVL_EDGE, evt_back); + + kempld_release_mutex(pld); + + gpio->mask = ~evt; + + /* Now check how many GPIO pins we have */ + for (i = 0; i < KEMPLD_GPIO_MAX_NUM; i++) { + if (evt & 0x1) + break; + evt >>= 1; + } + + chip->ngpio = i; + + return 0; +} + +static int kempld_gpio_probe(struct platform_device *pdev) +{ + struct kempld_device_data *pld; + struct kempld_gpio_data *gpio; + struct gpio_chip *chip; + int ret; + + pld = dev_get_drvdata(pdev->dev.parent); + + if (pld->info.spec_major < 2) { + dev_err(&pdev->dev, + "driver only supports GPIO devices " + "compatible to PLD spec. rev. 2.0 or higher\n"); + ret = -ENXIO; + goto err_check_rev; + } + + gpio = kzalloc(sizeof(*gpio), GFP_KERNEL); + if (gpio == NULL) { + dev_err(&pdev->dev, "unable to get memory for device data\n"); + ret = -ENOMEM; + goto err_alloc_dev_data; + } + + gpio->pld = pld; + + platform_set_drvdata(pdev, gpio); + + chip = &gpio->chip; + chip->label = "kempld-gpio"; + chip->owner = THIS_MODULE; + chip->dev = &pdev->dev; + chip->can_sleep = 1; + chip->base = gpiobase; + chip->ngpio = KEMPLD_GPIO_MAX_NUM; + + if (kempld_gpio_detect(gpio)) + dev_err(&pdev->dev, "GPIO detection failed\n"); + if (kempld_gpio_setup_event(gpio)) + dev_err(&pdev->dev, "Initializing events failed\n"); + + chip->direction_input = kempld_gpio_direction_input; + chip->direction_output = kempld_gpio_direction_output; + chip->get = kempld_gpio_get; + chip->set = kempld_gpio_set; + chip->dbg_show = kempld_gpio_dbg_show; + if (gpio->irq) + chip->to_irq = kempld_gpio_to_irq; + + ret = gpiochip_add(chip); + if (ret) { + dev_err(&pdev->dev, "Could not register GPIO chip\n"); + goto err_gpiochip_add; + } + + dev_info(&pdev->dev, + "GPIO functionality initialized with %d IOs mask (0x%04x)\n", + chip->ngpio, gpio->mask); + + return 0; + +err_gpiochip_add: + kfree(gpio); +err_alloc_dev_data: +err_check_rev: + + return ret; +} + +static int kempld_gpio_remove(struct platform_device *pdev) +{ + struct kempld_gpio_data *gpio = platform_get_drvdata(pdev); + int ret; + + ret = gpiochip_remove(&gpio->chip); + if (ret == 0) { + kfree(gpio); + platform_set_drvdata(pdev, NULL); + } + + return ret; +} + +static struct platform_driver kempld_gpio_driver = { + .driver = { + .name = "kempld-gpio", + .owner = THIS_MODULE, + }, + .probe = kempld_gpio_probe, + .remove = kempld_gpio_remove, +}; + +static int __init kempld_gpio_init(void) +{ + return platform_driver_register(&kempld_gpio_driver); +} + +static void __exit kempld_gpio_exit(void) +{ + platform_driver_unregister(&kempld_gpio_driver); +} + +module_init(kempld_gpio_init); +module_exit(kempld_gpio_exit); + +module_param(gpiobase, int, 0444); +module_param(gpioien, int, 0444); +module_param(gpioevt_lvl_edge, int, 0444); +module_param(gpioevt_low_high, int, 0444); +module_param(gpionmien, int, 0444); + +MODULE_DESCRIPTION("KEM PLD GPIO Driver"); +MODULE_AUTHOR("Michael Brunner <michael.brunner@kontron.com>"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:kempld_gpio"); +MODULE_PARM_DESC(gpiobase, "Set GPIO base (default -1=dynamic)"); +MODULE_PARM_DESC(gpioien, "Set GPIO IEN register (default 0x00)"); +MODULE_PARM_DESC(gpioevt_lvl_edge, + "Set GPIO EVT_LVL_EDGE register (default -1=no change)"); +MODULE_PARM_DESC(gpioevt_low_high, + "Set GPIO EVT_LOW_HIGH register (default -1=no change)"); +MODULE_PARM_DESC(gpionmien, "Set GPIO NMIEN register (default 0x00)"); + diff --git a/drivers/gpio/gpio-kempld.h b/drivers/gpio/gpio-kempld.h new file mode 100644 index 0000000..173932c --- /dev/null +++ b/drivers/gpio/gpio-kempld.h @@ -0,0 +1,50 @@ +/* + * kempld_gpio.h - Kontron PLD GPIO driver definitions + * + * Copyright (c) 2010-2012 Kontron Europe GmbH + * Author: Michael Brunner <michael.brunner@kontron.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License 2 as published + * by the Free Software Foundation. + * + * 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; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef _KEMPLD_GPIO_H_ +#define _KEMPLD_GPIO_H_ + +#define KEMPLD_GPIO_MAX_NUM 16 + +#define KEMPLD_GPIO_MASK(x) (1<<(x%8)) + +#define KEMPLD_GPIO_DIR 0x40 +#define KEMPLD_GPIO_DIR_NUM(x) (0x40+x/8) +#define KEMPLD_GPIO_LVL 0x42 +#define KEMPLD_GPIO_LVL_NUM(x) (0x42+x/8) +#define KEMPLD_GPIO_STS 0x44 +#define KEMPLD_GPIO_STS_NUM(x) (0x44+x/8) +#define KEMPLD_GPIO_EVT_LVL_EDGE 0x46 +#define KEMPLD_GPIO_EVT_LVL_EDGE_NUM(x) (0x46+x/8) +#define KEMPLD_GPIO_EVT_LOW_HIGH 0x48 +#define KEMPLD_GPIO_EVT_LOW_HIGH_NUM(x) (0x48+x/8) +#define KEMPLD_GPIO_IEN 0x4A +#define KEMPLD_GPIO_IEN_NUM(x) (0x4A+x/8) +#define KEMPLD_GPIO_NMIEN 0x4C +#define KEMPLD_GPIO_NMIEN_NUM(x) (0x4C+x/8) + +struct kempld_gpio_data { + struct gpio_chip chip; + int irq; + struct kempld_device_data *pld; + uint16_t mask; +}; + +#endif /* _KEMPLD_GPIO_H_ */ diff --git a/drivers/gpio/gpio-kempld_now1.c b/drivers/gpio/gpio-kempld_now1.c new file mode 100644 index 0000000..50ebd67 --- /dev/null +++ b/drivers/gpio/gpio-kempld_now1.c @@ -0,0 +1,280 @@ +/* + * kempld_now1_gpio.c - Kontron PLD GPIO driver for COMe-mSP1 + * + * Copyright (c) 2011-2012 Kontron Europe GmbH + * Author: Michael Brunner <michael.brunner@kontron.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License 2 as published + * by the Free Software Foundation. + * + * 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; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/errno.h> +#include <linux/acpi.h> +#include <linux/platform_device.h> +#include <linux/gpio.h> +#include <linux/mfd/kempld.h> +#include <linux/seq_file.h> + +#include "gpio-kempld.h" + +#define KEMPLD_NOW1_GPIO_MAX_NUM 8 + +#define KEMPLD_NOW1_FUNCTION 0x70 +#define KEMPLD_NOW1_FUNCTION_ALF_SDIO 0x01 +#define KEMPLD_NOW1_FUNCTION_USBCC 0x02 +#define KEMPLD_NOW1_GPIO_DIR 0xA0 +#define KEMPLD_NOW1_GPIO_LVL 0xA1 + +static int kempld_now1_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + struct kempld_device_data *pld; + int status; + + pld = dev_get_drvdata(chip->dev->parent); + + kempld_get_mutex_set_index(pld, KEMPLD_NOW1_GPIO_LVL); + + status = kempld_read8(pld, KEMPLD_NOW1_GPIO_LVL); + + kempld_release_mutex(pld); + + status &= KEMPLD_GPIO_MASK(offset); + + return status ? 1 : 0; +} + +static void kempld_now1_gpio_set(struct gpio_chip *chip, unsigned offset, + int value) +{ + struct kempld_device_data *pld; + int status; + + pld = dev_get_drvdata(chip->dev->parent); + + kempld_get_mutex_set_index(pld, KEMPLD_NOW1_GPIO_LVL); + + status = kempld_read8(pld, KEMPLD_NOW1_GPIO_LVL); + if (value) + status |= KEMPLD_GPIO_MASK(offset); + else + status &= ~KEMPLD_GPIO_MASK(offset); + kempld_write8(pld, KEMPLD_NOW1_GPIO_LVL, status); + + kempld_release_mutex(pld); +} + +static int kempld_now1_gpio_direction_input(struct gpio_chip *chip, + unsigned offset) +{ + struct kempld_device_data *pld; + int status; + + pld = dev_get_drvdata(chip->dev->parent); + + kempld_get_mutex_set_index(pld, KEMPLD_NOW1_GPIO_DIR); + + status = kempld_read8(pld, KEMPLD_NOW1_GPIO_DIR); + status &= ~KEMPLD_GPIO_MASK(offset); + kempld_write8(pld, KEMPLD_NOW1_GPIO_DIR, status); + + kempld_release_mutex(pld); + + return 0; +} + +static int kempld_now1_gpio_direction_output(struct gpio_chip *chip, + unsigned offset, int value) +{ + struct kempld_device_data *pld; + int status; + + pld = dev_get_drvdata(chip->dev->parent); + + kempld_get_mutex_set_index(pld, KEMPLD_NOW1_GPIO_DIR); + + status = kempld_read8(pld, KEMPLD_NOW1_GPIO_LVL); + if (value) + status |= KEMPLD_GPIO_MASK(offset); + else + status &= ~KEMPLD_GPIO_MASK(offset); + kempld_write8(pld, KEMPLD_NOW1_GPIO_LVL, status); + + status = kempld_read8(pld, KEMPLD_NOW1_GPIO_DIR); + status |= KEMPLD_GPIO_MASK(offset); + kempld_write8(pld, KEMPLD_NOW1_GPIO_DIR, status); + + kempld_release_mutex(pld); + + return 0; +} + +#ifdef CONFIG_DEBUG_FS +static int kempld_now1_gpio_get_direction(struct gpio_chip *chip, + unsigned offset) +{ + struct kempld_device_data *pld; + int status; + + pld = dev_get_drvdata(chip->dev->parent); + + kempld_get_mutex_set_index(pld, KEMPLD_NOW1_GPIO_DIR); + + status = kempld_read8(pld, KEMPLD_NOW1_GPIO_DIR); + + kempld_release_mutex(pld); + + status &= KEMPLD_GPIO_MASK(offset); + + return status ? 1 : 0; +} + +static void kempld_now1_gpio_dbg_show(struct seq_file *s, + struct gpio_chip *chip) +{ + struct kempld_device_data *pld; + int function; + int i; + + pld = dev_get_drvdata(chip->dev->parent); + + kempld_get_mutex_set_index(pld, KEMPLD_NOW1_FUNCTION); + + function = kempld_read8(pld, KEMPLD_NOW1_FUNCTION); + + kempld_release_mutex(pld); + + for (i = 0; i < chip->ngpio; i++) { + int gpio = i + chip->base; + + seq_printf(s, " gpio-%-3d %s %s %s\n", gpio, + kempld_now1_gpio_get_direction(chip, i) + ? "out" : "in", + kempld_now1_gpio_get(chip, i) + ? "hi" : "lo", + function & KEMPLD_NOW1_FUNCTION_ALF_SDIO + ? "sdio" : + ((function & KEMPLD_NOW1_FUNCTION_USBCC) + && (i == 0)) + ? "usbcc" : "gpio"); + } +} +#else +#define kempld_now1_gpio_dbg_show NULL +#endif + +static int kempld_now1_gpio_probe(struct platform_device *pdev) +{ + struct kempld_device_data *pld; + struct gpio_chip *chip; + int function; + int ret; + + pld = dev_get_drvdata(pdev->dev.parent); + + chip = kzalloc(sizeof(struct gpio_chip), GFP_KERNEL); + if (chip == NULL) { + dev_err(&pdev->dev, "unable to get memory for device data\n"); + ret = -ENOMEM; + goto err_alloc_dev_data; + } + + chip->label = "kempld_now1-gpio"; + chip->owner = THIS_MODULE; + chip->can_sleep = 1; + chip->dev = &pdev->dev; + chip->base = -1; + chip->ngpio = KEMPLD_NOW1_GPIO_MAX_NUM; + + chip->direction_input = kempld_now1_gpio_direction_input; + chip->direction_output = kempld_now1_gpio_direction_output; + chip->get = kempld_now1_gpio_get; + chip->set = kempld_now1_gpio_set; + chip->dbg_show = kempld_now1_gpio_dbg_show; + + kempld_get_mutex_set_index(pld, KEMPLD_NOW1_FUNCTION); + + function = kempld_read8(pld, KEMPLD_NOW1_FUNCTION); + + kempld_release_mutex(pld); + + if (function & KEMPLD_NOW1_FUNCTION_ALF_SDIO) + dev_warn(&pdev->dev, "GPIO pins are used for SDIO\n"); + if (function & KEMPLD_NOW1_FUNCTION_USBCC) { + dev_info(&pdev->dev, + "GPI[0] is forwarded to USB cable connect\n"); + } + + platform_set_drvdata(pdev, chip); + + ret = gpiochip_add(chip); + if (ret) { + dev_err(&pdev->dev, "Could not register GPIO chip\n"); + goto err_gpiochip_add; + } + + dev_info(&pdev->dev, "GPIO functionality initialized\n"); + + return 0; + +err_gpiochip_add: + kfree(chip); +err_alloc_dev_data: + + return ret; +} + +static int kempld_now1_gpio_remove(struct platform_device *pdev) +{ + struct gpio_chip *chip = platform_get_drvdata(pdev); + int ret; + + ret = gpiochip_remove(chip); + if (ret == 0) { + kfree(chip); + platform_set_drvdata(pdev, NULL); + } + + return ret; +} + +static struct platform_driver kempld_now1_gpio_driver = { + .driver = { + .name = "kempld_now1-gpio", + .owner = THIS_MODULE, + }, + .probe = kempld_now1_gpio_probe, + .remove = kempld_now1_gpio_remove, +}; + +static int __init kempld_now1_gpio_init(void) +{ + return platform_driver_register(&kempld_now1_gpio_driver); +} + +static void __exit kempld_now1_gpio_exit(void) +{ + platform_driver_unregister(&kempld_now1_gpio_driver); +} + +module_init(kempld_now1_gpio_init); +module_exit(kempld_now1_gpio_exit); + +MODULE_DESCRIPTION("KEM PLD nanoETXexpress-SP GPIO Driver"); +MODULE_AUTHOR("Michael Brunner <michael.brunner@kontron.com>"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:kempld_now1-gpio"); -- 1.7.9.5
WARNING: multiple messages have this Message-ID (diff)
From: Kevin Strasser <kevin.strasser-VuQAYsv1563Yd54FQh9/CA@public.gmane.org> To: linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org Cc: Michael Brunner <michael.brunner-2UyDCMiLNfhBDgjK7y7TUQ@public.gmane.org>, Samuel Ortiz <sameo-VuQAYsv1563Yd54FQh9/CA@public.gmane.org>, Wolfram Sang <wsa-z923LK4zBo2bacvFa/9K2g@public.gmane.org>, Ben Dooks <ben-linux-elnMNo+KYs3YtjvyW6yDsg@public.gmane.org>, linux-i2c-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, Grant Likely <grant.likely-s3s/WqlpOiPyB63q8FvJNQ@public.gmane.org>, Linus Walleij <linus.walleij-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>, Wim Van Sebroeck <wim-IQzOog9fTRqzQB+pC5nmwQ@public.gmane.org>, linux-watchdog-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, Darren Hart <dvhart-VuQAYsv1563Yd54FQh9/CA@public.gmane.org>, Michael Brunner <mibru-Mmb7MZpHnFY@public.gmane.org>, Greg Kroah-Hartman <gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r@public.gmane.org>, Kevin Strasser <kevin.strasser-VuQAYsv1563Yd54FQh9/CA@public.gmane.org> Subject: [PATCH 3/4] gpio: Kontron PLD gpio driver Date: Mon, 8 Apr 2013 10:15:20 -0700 [thread overview] Message-ID: <1365441321-21952-3-git-send-email-kevin.strasser@linux.intel.com> (raw) In-Reply-To: <1365441321-21952-1-git-send-email-kevin.strasser-VuQAYsv1563Yd54FQh9/CA@public.gmane.org> From: Michael Brunner <michael.brunner-2UyDCMiLNfhBDgjK7y7TUQ@public.gmane.org> Add gpio support for the on-board PLD found on some Kontron embedded modules. Signed-off-by: Michael Brunner <michael.brunner-2UyDCMiLNfhBDgjK7y7TUQ@public.gmane.org> Signed-off-by: Kevin Strasser <kevin.strasser-VuQAYsv1563Yd54FQh9/CA@public.gmane.org> --- drivers/gpio/Kconfig | 22 ++ drivers/gpio/Makefile | 2 + drivers/gpio/gpio-kempld.c | 476 +++++++++++++++++++++++++++++++++++++++ drivers/gpio/gpio-kempld.h | 50 ++++ drivers/gpio/gpio-kempld_now1.c | 280 +++++++++++++++++++++++ 5 files changed, 830 insertions(+) create mode 100644 drivers/gpio/gpio-kempld.c create mode 100644 drivers/gpio/gpio-kempld.h create mode 100644 drivers/gpio/gpio-kempld_now1.c diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 93aaadf..88e1bc9 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -658,6 +658,28 @@ config GPIO_UCB1400 This enables support for the Philips UCB1400 GPIO pins. The UCB1400 is an AC97 audio codec. +comment "LPC GPIO expanders:" + +config GPIO_KEMPLD + tristate "Kontron COM GPIO" + depends on MFD_KEMPLD + help + This enables support for the PLD GPIO interface on some Kontron ETX + and COMexpress (ETXexpress) modules. + + This driver can also be built as a module. If so, the module will + be called gpio-kempld. + +config GPIO_NOW1_KEMPLD + tristate "Kontron COMe-mSP1 (nanoETXexpress-SP) GPIO" + depends on MFD_KEMPLD + help + This enables support for the PLD GPIO interface on the Kontron + COMe-mSP1 (nanoETXexpress-SP) module. + + This driver can also be built as a module. If so, the module will + be called gpio-kempld_now1. + comment "MODULbus GPIO expanders:" config GPIO_JANZ_TTL diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 22e07bc..59919db 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -28,6 +28,8 @@ obj-$(CONFIG_GPIO_GE_FPGA) += gpio-ge.o obj-$(CONFIG_GPIO_ICH) += gpio-ich.o obj-$(CONFIG_GPIO_IT8761E) += gpio-it8761e.o obj-$(CONFIG_GPIO_JANZ_TTL) += gpio-janz-ttl.o +obj-$(CONFIG_GPIO_KEMPLD) += gpio-kempld.o +obj-$(CONFIG_GPIO_NOW1_KEMPLD) += gpio-kempld_now1.o obj-$(CONFIG_ARCH_KS8695) += gpio-ks8695.o obj-$(CONFIG_GPIO_LANGWELL) += gpio-langwell.o obj-$(CONFIG_ARCH_LPC32XX) += gpio-lpc32xx.o diff --git a/drivers/gpio/gpio-kempld.c b/drivers/gpio/gpio-kempld.c new file mode 100644 index 0000000..e18967d --- /dev/null +++ b/drivers/gpio/gpio-kempld.c @@ -0,0 +1,476 @@ +/* + * kempld_gpio.c - Kontron PLD GPIO driver + * + * Copyright (c) 2010-2012 Kontron Europe GmbH + * Author: Michael Brunner <michael.brunner-2UyDCMiLNfhBDgjK7y7TUQ@public.gmane.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License 2 as published + * by the Free Software Foundation. + * + * 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; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/errno.h> +#include <linux/acpi.h> +#include <linux/platform_device.h> +#include <linux/gpio.h> +#include <linux/mfd/kempld.h> +#include <linux/seq_file.h> + +#include "gpio-kempld.h" + +static int gpiobase = -1; +static int gpioien = 0x00; +static int gpioevt_lvl_edge = -1; +static int gpioevt_low_high = -1; +static int gpionmien = 0x00; + +static int kempld_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + struct kempld_gpio_data *gpio + = container_of(chip, struct kempld_gpio_data, chip); + struct kempld_device_data *pld = gpio->pld; + int status; + + pld = gpio->pld; + + kempld_get_mutex_set_index(pld, KEMPLD_GPIO_LVL_NUM(offset)); + + status = kempld_read8(pld, KEMPLD_GPIO_LVL_NUM(offset)); + status &= KEMPLD_GPIO_MASK(offset); + + kempld_release_mutex(pld); + + return status; +} + +static void kempld_gpio_set(struct gpio_chip *chip, unsigned offset, + int value) +{ + struct kempld_gpio_data *gpio + = container_of(chip, struct kempld_gpio_data, chip); + struct kempld_device_data *pld = gpio->pld; + int status; + + + kempld_get_mutex_set_index(pld, KEMPLD_GPIO_LVL_NUM(offset)); + + status = kempld_read8(pld, KEMPLD_GPIO_LVL_NUM(offset)); + if (value) + status |= KEMPLD_GPIO_MASK(offset); + else + status &= ~KEMPLD_GPIO_MASK(offset); + kempld_write8(pld, KEMPLD_GPIO_LVL_NUM(offset), status); + + kempld_release_mutex(pld); +} + +static int kempld_gpio_direction_input(struct gpio_chip *chip, unsigned offset) +{ + struct kempld_gpio_data *gpio + = container_of(chip, struct kempld_gpio_data, chip); + struct kempld_device_data *pld = gpio->pld; + int status; + + kempld_get_mutex_set_index(pld, KEMPLD_GPIO_DIR_NUM(offset)); + + status = kempld_read8(pld, KEMPLD_GPIO_DIR_NUM(offset)); + status &= ~KEMPLD_GPIO_MASK(offset); + kempld_write8(pld, KEMPLD_GPIO_DIR_NUM(offset), status); + + kempld_release_mutex(pld); + + + return 0; +} + +static int kempld_gpio_direction_output(struct gpio_chip *chip, + unsigned offset, int value) +{ + struct kempld_gpio_data *gpio + = container_of(chip, struct kempld_gpio_data, chip); + struct kempld_device_data *pld = gpio->pld; + int status; + + kempld_get_mutex_set_index(pld, KEMPLD_GPIO_LVL_NUM(offset)); + + status = kempld_read8(pld, KEMPLD_GPIO_LVL_NUM(offset)); + if (value) + status |= KEMPLD_GPIO_MASK(offset); + else + status &= ~KEMPLD_GPIO_MASK(offset); + kempld_write8(pld, KEMPLD_GPIO_LVL_NUM(offset), status); + + status = kempld_read8(pld, KEMPLD_GPIO_DIR_NUM(offset)); + status |= KEMPLD_GPIO_MASK(offset); + kempld_write8(pld, KEMPLD_GPIO_DIR_NUM(offset), status); + + kempld_release_mutex(pld); + + return 0; +} + +static int kempld_gpio_to_irq(struct gpio_chip *chip, unsigned offset) +{ + struct kempld_gpio_data *gpio + = container_of(chip, struct kempld_gpio_data, chip); + return gpio->irq; +} + +#ifdef CONFIG_DEBUG_FS +static int kempld_gpio_get_direction(struct gpio_chip *chip, unsigned offset) +{ + struct kempld_gpio_data *gpio + = container_of(chip, struct kempld_gpio_data, chip); + struct kempld_device_data *pld = gpio->pld; + int status; + + kempld_get_mutex_set_index(pld, KEMPLD_GPIO_DIR_NUM(offset)); + + status = kempld_read8(pld, KEMPLD_GPIO_DIR_NUM(offset)); + status &= KEMPLD_GPIO_MASK(offset); + + kempld_release_mutex(pld); + + + return status ? 1 : 0; +} + +static int kempld_gpio_get_ien(struct gpio_chip *chip, unsigned offset) +{ + struct kempld_gpio_data *gpio + = container_of(chip, struct kempld_gpio_data, chip); + struct kempld_device_data *pld = gpio->pld; + int status; + + kempld_get_mutex_set_index(pld, KEMPLD_GPIO_IEN_NUM(offset)); + + status = kempld_read8(pld, KEMPLD_GPIO_IEN_NUM(offset)); + status &= KEMPLD_GPIO_MASK(offset); + + kempld_release_mutex(pld); + + + return status ? 1 : 0; +} + +static int kempld_gpio_get_evt_lvl_edge(struct gpio_chip *chip, + unsigned offset) +{ + struct kempld_gpio_data *gpio + = container_of(chip, struct kempld_gpio_data, chip); + struct kempld_device_data *pld = gpio->pld; + int status; + + kempld_get_mutex_set_index(pld, KEMPLD_GPIO_EVT_LVL_EDGE_NUM(offset)); + + status = kempld_read8(pld, KEMPLD_GPIO_EVT_LVL_EDGE_NUM(offset)); + status &= KEMPLD_GPIO_MASK(offset); + + kempld_release_mutex(pld); + + + return status ? 1 : 0; +} + +static int kempld_gpio_get_evt_high_low(struct gpio_chip *chip, + unsigned offset) +{ + struct kempld_gpio_data *gpio + = container_of(chip, struct kempld_gpio_data, chip); + struct kempld_device_data *pld = gpio->pld; + int status; + + gpio = dev_get_drvdata(chip->dev); + pld = gpio->pld; + + kempld_get_mutex_set_index(pld, KEMPLD_GPIO_EVT_LOW_HIGH_NUM(offset)); + + status = kempld_read8(pld, KEMPLD_GPIO_EVT_LOW_HIGH_NUM(offset)); + status &= KEMPLD_GPIO_MASK(offset); + + kempld_release_mutex(pld); + + + return status ? 1 : 0; +} + +static int kempld_gpio_get_nmien(struct gpio_chip *chip, unsigned offset) +{ + struct kempld_gpio_data *gpio + = container_of(chip, struct kempld_gpio_data, chip); + struct kempld_device_data *pld = gpio->pld; + int status; + + gpio = dev_get_drvdata(chip->dev); + pld = gpio->pld; + + kempld_get_mutex_set_index(pld, KEMPLD_GPIO_NMIEN_NUM(offset)); + + status = kempld_read8(pld, KEMPLD_GPIO_NMIEN_NUM(offset)); + status &= KEMPLD_GPIO_MASK(offset); + + kempld_release_mutex(pld); + + + return status ? 1 : 0; +} + +static void kempld_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) +{ + int i; + + for (i = 0; i < chip->ngpio; i++) { + int gpio = i + chip->base; + + seq_printf(s, " gpio-%-3d %s %s", gpio, + kempld_gpio_get_direction(chip, i) + ? "out" : "in", + kempld_gpio_get(chip, i) + ? "hi" : "lo"); + seq_printf(s, ", event on %s (irq %s, nmi %s)\n", + (kempld_gpio_get_evt_lvl_edge(chip, i) + ? (kempld_gpio_get_evt_high_low(chip, i) + ? "rising edge" : "falling edge") : + (kempld_gpio_get_evt_high_low(chip, i) + ? "high level" : "low level")), + kempld_gpio_get_ien(chip, i) + ? "enabled" : "disabled", + kempld_gpio_get_nmien(chip, i) + ? "enabled" : "disabled"); + } +} +#else +#define kempld_gpio_dbg_show NULL +#endif + +static int kempld_gpio_setup_event(struct kempld_gpio_data *gpio) +{ + struct kempld_device_data *pld = gpio->pld; + struct gpio_chip *chip = &gpio->chip; + int irq; + + irq = gpio->irq; + + kempld_get_mutex_set_index(pld, KEMPLD_IRQ_GPIO); + irq = kempld_read8(pld, KEMPLD_IRQ_GPIO); + + /* Leave if interrupts are not supported by the GPIO core */ + if ((irq & 0xf0) == 0xf0) + return 0; + + gpio->irq = irq & 0x0f; + + if (gpioien & !gpio->mask) { + dev_err(chip->dev, "gpioien parameter is invalid"); + gpio->irq = 0; + } + + if (gpionmien & !gpio->mask) { + dev_err(chip->dev, "gpionmien parameter is invalid"); + gpio->irq = 0; + } + + if ((gpioevt_lvl_edge & !gpio->mask) && (gpioevt_lvl_edge != -1)) { + dev_err(chip->dev, "gpioevt_lvl_edge parameter is invalid"); + gpio->irq = 0; + } + + if ((gpioevt_low_high & !gpio->mask) && (gpioevt_low_high != -1)) { + dev_err(chip->dev, "gpioevt_low_high parameter is invalid"); + gpio->irq = 0; + } + + if (!gpio->irq) + return -EIO; + + if (gpioevt_lvl_edge != -1) + kempld_write8(pld, KEMPLD_GPIO_EVT_LVL_EDGE, gpioevt_lvl_edge); + + if (gpioevt_low_high != -1) + kempld_write8(pld, KEMPLD_GPIO_EVT_LOW_HIGH, gpioevt_low_high); + + kempld_write8(pld, KEMPLD_GPIO_NMIEN, gpionmien); + kempld_write8(pld, KEMPLD_GPIO_IEN, gpioien); + + kempld_release_mutex(pld); + + return 0; +} + + +static int kempld_gpio_detect(struct kempld_gpio_data *gpio) +{ + struct kempld_device_data *pld = gpio->pld; + struct gpio_chip *chip = &gpio->chip; + u16 evt, evt_back; + int i; + + kempld_get_mutex_set_index(pld, KEMPLD_GPIO_IEN); + + /* Backup event register as it might be already initialized */ + evt_back = kempld_read16(pld, KEMPLD_GPIO_EVT_LVL_EDGE); + + /* Disable interrupt enables and set event register to zero */ + kempld_write16(pld, KEMPLD_GPIO_IEN, 0x0000); + kempld_write16(pld, KEMPLD_GPIO_NMIEN, 0x0000); + kempld_write16(pld, KEMPLD_GPIO_EVT_LVL_EDGE, 0x0000); + + /* Read back event register */ + evt = kempld_read16(pld, KEMPLD_GPIO_EVT_LVL_EDGE); + + /* Restore event register */ + kempld_write16(pld, KEMPLD_GPIO_EVT_LVL_EDGE, evt_back); + + kempld_release_mutex(pld); + + gpio->mask = ~evt; + + /* Now check how many GPIO pins we have */ + for (i = 0; i < KEMPLD_GPIO_MAX_NUM; i++) { + if (evt & 0x1) + break; + evt >>= 1; + } + + chip->ngpio = i; + + return 0; +} + +static int kempld_gpio_probe(struct platform_device *pdev) +{ + struct kempld_device_data *pld; + struct kempld_gpio_data *gpio; + struct gpio_chip *chip; + int ret; + + pld = dev_get_drvdata(pdev->dev.parent); + + if (pld->info.spec_major < 2) { + dev_err(&pdev->dev, + "driver only supports GPIO devices " + "compatible to PLD spec. rev. 2.0 or higher\n"); + ret = -ENXIO; + goto err_check_rev; + } + + gpio = kzalloc(sizeof(*gpio), GFP_KERNEL); + if (gpio == NULL) { + dev_err(&pdev->dev, "unable to get memory for device data\n"); + ret = -ENOMEM; + goto err_alloc_dev_data; + } + + gpio->pld = pld; + + platform_set_drvdata(pdev, gpio); + + chip = &gpio->chip; + chip->label = "kempld-gpio"; + chip->owner = THIS_MODULE; + chip->dev = &pdev->dev; + chip->can_sleep = 1; + chip->base = gpiobase; + chip->ngpio = KEMPLD_GPIO_MAX_NUM; + + if (kempld_gpio_detect(gpio)) + dev_err(&pdev->dev, "GPIO detection failed\n"); + if (kempld_gpio_setup_event(gpio)) + dev_err(&pdev->dev, "Initializing events failed\n"); + + chip->direction_input = kempld_gpio_direction_input; + chip->direction_output = kempld_gpio_direction_output; + chip->get = kempld_gpio_get; + chip->set = kempld_gpio_set; + chip->dbg_show = kempld_gpio_dbg_show; + if (gpio->irq) + chip->to_irq = kempld_gpio_to_irq; + + ret = gpiochip_add(chip); + if (ret) { + dev_err(&pdev->dev, "Could not register GPIO chip\n"); + goto err_gpiochip_add; + } + + dev_info(&pdev->dev, + "GPIO functionality initialized with %d IOs mask (0x%04x)\n", + chip->ngpio, gpio->mask); + + return 0; + +err_gpiochip_add: + kfree(gpio); +err_alloc_dev_data: +err_check_rev: + + return ret; +} + +static int kempld_gpio_remove(struct platform_device *pdev) +{ + struct kempld_gpio_data *gpio = platform_get_drvdata(pdev); + int ret; + + ret = gpiochip_remove(&gpio->chip); + if (ret == 0) { + kfree(gpio); + platform_set_drvdata(pdev, NULL); + } + + return ret; +} + +static struct platform_driver kempld_gpio_driver = { + .driver = { + .name = "kempld-gpio", + .owner = THIS_MODULE, + }, + .probe = kempld_gpio_probe, + .remove = kempld_gpio_remove, +}; + +static int __init kempld_gpio_init(void) +{ + return platform_driver_register(&kempld_gpio_driver); +} + +static void __exit kempld_gpio_exit(void) +{ + platform_driver_unregister(&kempld_gpio_driver); +} + +module_init(kempld_gpio_init); +module_exit(kempld_gpio_exit); + +module_param(gpiobase, int, 0444); +module_param(gpioien, int, 0444); +module_param(gpioevt_lvl_edge, int, 0444); +module_param(gpioevt_low_high, int, 0444); +module_param(gpionmien, int, 0444); + +MODULE_DESCRIPTION("KEM PLD GPIO Driver"); +MODULE_AUTHOR("Michael Brunner <michael.brunner-2UyDCMiLNfhBDgjK7y7TUQ@public.gmane.org>"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:kempld_gpio"); +MODULE_PARM_DESC(gpiobase, "Set GPIO base (default -1=dynamic)"); +MODULE_PARM_DESC(gpioien, "Set GPIO IEN register (default 0x00)"); +MODULE_PARM_DESC(gpioevt_lvl_edge, + "Set GPIO EVT_LVL_EDGE register (default -1=no change)"); +MODULE_PARM_DESC(gpioevt_low_high, + "Set GPIO EVT_LOW_HIGH register (default -1=no change)"); +MODULE_PARM_DESC(gpionmien, "Set GPIO NMIEN register (default 0x00)"); + diff --git a/drivers/gpio/gpio-kempld.h b/drivers/gpio/gpio-kempld.h new file mode 100644 index 0000000..173932c --- /dev/null +++ b/drivers/gpio/gpio-kempld.h @@ -0,0 +1,50 @@ +/* + * kempld_gpio.h - Kontron PLD GPIO driver definitions + * + * Copyright (c) 2010-2012 Kontron Europe GmbH + * Author: Michael Brunner <michael.brunner-2UyDCMiLNfhBDgjK7y7TUQ@public.gmane.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License 2 as published + * by the Free Software Foundation. + * + * 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; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef _KEMPLD_GPIO_H_ +#define _KEMPLD_GPIO_H_ + +#define KEMPLD_GPIO_MAX_NUM 16 + +#define KEMPLD_GPIO_MASK(x) (1<<(x%8)) + +#define KEMPLD_GPIO_DIR 0x40 +#define KEMPLD_GPIO_DIR_NUM(x) (0x40+x/8) +#define KEMPLD_GPIO_LVL 0x42 +#define KEMPLD_GPIO_LVL_NUM(x) (0x42+x/8) +#define KEMPLD_GPIO_STS 0x44 +#define KEMPLD_GPIO_STS_NUM(x) (0x44+x/8) +#define KEMPLD_GPIO_EVT_LVL_EDGE 0x46 +#define KEMPLD_GPIO_EVT_LVL_EDGE_NUM(x) (0x46+x/8) +#define KEMPLD_GPIO_EVT_LOW_HIGH 0x48 +#define KEMPLD_GPIO_EVT_LOW_HIGH_NUM(x) (0x48+x/8) +#define KEMPLD_GPIO_IEN 0x4A +#define KEMPLD_GPIO_IEN_NUM(x) (0x4A+x/8) +#define KEMPLD_GPIO_NMIEN 0x4C +#define KEMPLD_GPIO_NMIEN_NUM(x) (0x4C+x/8) + +struct kempld_gpio_data { + struct gpio_chip chip; + int irq; + struct kempld_device_data *pld; + uint16_t mask; +}; + +#endif /* _KEMPLD_GPIO_H_ */ diff --git a/drivers/gpio/gpio-kempld_now1.c b/drivers/gpio/gpio-kempld_now1.c new file mode 100644 index 0000000..50ebd67 --- /dev/null +++ b/drivers/gpio/gpio-kempld_now1.c @@ -0,0 +1,280 @@ +/* + * kempld_now1_gpio.c - Kontron PLD GPIO driver for COMe-mSP1 + * + * Copyright (c) 2011-2012 Kontron Europe GmbH + * Author: Michael Brunner <michael.brunner-2UyDCMiLNfhBDgjK7y7TUQ@public.gmane.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License 2 as published + * by the Free Software Foundation. + * + * 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; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/errno.h> +#include <linux/acpi.h> +#include <linux/platform_device.h> +#include <linux/gpio.h> +#include <linux/mfd/kempld.h> +#include <linux/seq_file.h> + +#include "gpio-kempld.h" + +#define KEMPLD_NOW1_GPIO_MAX_NUM 8 + +#define KEMPLD_NOW1_FUNCTION 0x70 +#define KEMPLD_NOW1_FUNCTION_ALF_SDIO 0x01 +#define KEMPLD_NOW1_FUNCTION_USBCC 0x02 +#define KEMPLD_NOW1_GPIO_DIR 0xA0 +#define KEMPLD_NOW1_GPIO_LVL 0xA1 + +static int kempld_now1_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + struct kempld_device_data *pld; + int status; + + pld = dev_get_drvdata(chip->dev->parent); + + kempld_get_mutex_set_index(pld, KEMPLD_NOW1_GPIO_LVL); + + status = kempld_read8(pld, KEMPLD_NOW1_GPIO_LVL); + + kempld_release_mutex(pld); + + status &= KEMPLD_GPIO_MASK(offset); + + return status ? 1 : 0; +} + +static void kempld_now1_gpio_set(struct gpio_chip *chip, unsigned offset, + int value) +{ + struct kempld_device_data *pld; + int status; + + pld = dev_get_drvdata(chip->dev->parent); + + kempld_get_mutex_set_index(pld, KEMPLD_NOW1_GPIO_LVL); + + status = kempld_read8(pld, KEMPLD_NOW1_GPIO_LVL); + if (value) + status |= KEMPLD_GPIO_MASK(offset); + else + status &= ~KEMPLD_GPIO_MASK(offset); + kempld_write8(pld, KEMPLD_NOW1_GPIO_LVL, status); + + kempld_release_mutex(pld); +} + +static int kempld_now1_gpio_direction_input(struct gpio_chip *chip, + unsigned offset) +{ + struct kempld_device_data *pld; + int status; + + pld = dev_get_drvdata(chip->dev->parent); + + kempld_get_mutex_set_index(pld, KEMPLD_NOW1_GPIO_DIR); + + status = kempld_read8(pld, KEMPLD_NOW1_GPIO_DIR); + status &= ~KEMPLD_GPIO_MASK(offset); + kempld_write8(pld, KEMPLD_NOW1_GPIO_DIR, status); + + kempld_release_mutex(pld); + + return 0; +} + +static int kempld_now1_gpio_direction_output(struct gpio_chip *chip, + unsigned offset, int value) +{ + struct kempld_device_data *pld; + int status; + + pld = dev_get_drvdata(chip->dev->parent); + + kempld_get_mutex_set_index(pld, KEMPLD_NOW1_GPIO_DIR); + + status = kempld_read8(pld, KEMPLD_NOW1_GPIO_LVL); + if (value) + status |= KEMPLD_GPIO_MASK(offset); + else + status &= ~KEMPLD_GPIO_MASK(offset); + kempld_write8(pld, KEMPLD_NOW1_GPIO_LVL, status); + + status = kempld_read8(pld, KEMPLD_NOW1_GPIO_DIR); + status |= KEMPLD_GPIO_MASK(offset); + kempld_write8(pld, KEMPLD_NOW1_GPIO_DIR, status); + + kempld_release_mutex(pld); + + return 0; +} + +#ifdef CONFIG_DEBUG_FS +static int kempld_now1_gpio_get_direction(struct gpio_chip *chip, + unsigned offset) +{ + struct kempld_device_data *pld; + int status; + + pld = dev_get_drvdata(chip->dev->parent); + + kempld_get_mutex_set_index(pld, KEMPLD_NOW1_GPIO_DIR); + + status = kempld_read8(pld, KEMPLD_NOW1_GPIO_DIR); + + kempld_release_mutex(pld); + + status &= KEMPLD_GPIO_MASK(offset); + + return status ? 1 : 0; +} + +static void kempld_now1_gpio_dbg_show(struct seq_file *s, + struct gpio_chip *chip) +{ + struct kempld_device_data *pld; + int function; + int i; + + pld = dev_get_drvdata(chip->dev->parent); + + kempld_get_mutex_set_index(pld, KEMPLD_NOW1_FUNCTION); + + function = kempld_read8(pld, KEMPLD_NOW1_FUNCTION); + + kempld_release_mutex(pld); + + for (i = 0; i < chip->ngpio; i++) { + int gpio = i + chip->base; + + seq_printf(s, " gpio-%-3d %s %s %s\n", gpio, + kempld_now1_gpio_get_direction(chip, i) + ? "out" : "in", + kempld_now1_gpio_get(chip, i) + ? "hi" : "lo", + function & KEMPLD_NOW1_FUNCTION_ALF_SDIO + ? "sdio" : + ((function & KEMPLD_NOW1_FUNCTION_USBCC) + && (i == 0)) + ? "usbcc" : "gpio"); + } +} +#else +#define kempld_now1_gpio_dbg_show NULL +#endif + +static int kempld_now1_gpio_probe(struct platform_device *pdev) +{ + struct kempld_device_data *pld; + struct gpio_chip *chip; + int function; + int ret; + + pld = dev_get_drvdata(pdev->dev.parent); + + chip = kzalloc(sizeof(struct gpio_chip), GFP_KERNEL); + if (chip == NULL) { + dev_err(&pdev->dev, "unable to get memory for device data\n"); + ret = -ENOMEM; + goto err_alloc_dev_data; + } + + chip->label = "kempld_now1-gpio"; + chip->owner = THIS_MODULE; + chip->can_sleep = 1; + chip->dev = &pdev->dev; + chip->base = -1; + chip->ngpio = KEMPLD_NOW1_GPIO_MAX_NUM; + + chip->direction_input = kempld_now1_gpio_direction_input; + chip->direction_output = kempld_now1_gpio_direction_output; + chip->get = kempld_now1_gpio_get; + chip->set = kempld_now1_gpio_set; + chip->dbg_show = kempld_now1_gpio_dbg_show; + + kempld_get_mutex_set_index(pld, KEMPLD_NOW1_FUNCTION); + + function = kempld_read8(pld, KEMPLD_NOW1_FUNCTION); + + kempld_release_mutex(pld); + + if (function & KEMPLD_NOW1_FUNCTION_ALF_SDIO) + dev_warn(&pdev->dev, "GPIO pins are used for SDIO\n"); + if (function & KEMPLD_NOW1_FUNCTION_USBCC) { + dev_info(&pdev->dev, + "GPI[0] is forwarded to USB cable connect\n"); + } + + platform_set_drvdata(pdev, chip); + + ret = gpiochip_add(chip); + if (ret) { + dev_err(&pdev->dev, "Could not register GPIO chip\n"); + goto err_gpiochip_add; + } + + dev_info(&pdev->dev, "GPIO functionality initialized\n"); + + return 0; + +err_gpiochip_add: + kfree(chip); +err_alloc_dev_data: + + return ret; +} + +static int kempld_now1_gpio_remove(struct platform_device *pdev) +{ + struct gpio_chip *chip = platform_get_drvdata(pdev); + int ret; + + ret = gpiochip_remove(chip); + if (ret == 0) { + kfree(chip); + platform_set_drvdata(pdev, NULL); + } + + return ret; +} + +static struct platform_driver kempld_now1_gpio_driver = { + .driver = { + .name = "kempld_now1-gpio", + .owner = THIS_MODULE, + }, + .probe = kempld_now1_gpio_probe, + .remove = kempld_now1_gpio_remove, +}; + +static int __init kempld_now1_gpio_init(void) +{ + return platform_driver_register(&kempld_now1_gpio_driver); +} + +static void __exit kempld_now1_gpio_exit(void) +{ + platform_driver_unregister(&kempld_now1_gpio_driver); +} + +module_init(kempld_now1_gpio_init); +module_exit(kempld_now1_gpio_exit); + +MODULE_DESCRIPTION("KEM PLD nanoETXexpress-SP GPIO Driver"); +MODULE_AUTHOR("Michael Brunner <michael.brunner-2UyDCMiLNfhBDgjK7y7TUQ@public.gmane.org>"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:kempld_now1-gpio"); -- 1.7.9.5
next prev parent reply other threads:[~2013-04-08 17:18 UTC|newest] Thread overview: 67+ messages / expand[flat|nested] mbox.gz Atom feed top 2013-04-08 17:15 [PATCH 1/4] mfd: Kontron PLD mfd driver Kevin Strasser 2013-04-08 17:15 ` [PATCH 2/4] i2c: Kontron PLD i2c bus driver Kevin Strasser 2013-04-08 17:15 ` Kevin Strasser 2013-04-10 17:02 ` Guenter Roeck 2013-04-10 17:02 ` Guenter Roeck 2013-04-16 9:53 ` Wolfram Sang 2013-04-16 9:53 ` Wolfram Sang 2013-04-08 17:15 ` Kevin Strasser [this message] 2013-04-08 17:15 ` [PATCH 3/4] gpio: Kontron PLD gpio driver Kevin Strasser 2013-04-09 8:46 ` Linus Walleij 2013-04-09 16:41 ` Guenter Roeck 2013-04-09 16:41 ` Guenter Roeck 2013-04-10 20:06 ` Linus Walleij 2013-04-10 20:45 ` Linus Walleij 2013-04-12 11:09 ` Michael Brunner 2013-04-12 11:09 ` Michael Brunner 2013-04-12 22:05 ` Linus Walleij 2013-04-08 17:15 ` [PATCH 4/4] watchdog: Kontron PLD watchdog timer Kevin Strasser 2013-04-08 17:15 ` Kevin Strasser 2013-04-10 16:47 ` Guenter Roeck 2013-04-10 16:57 ` Kevin Strasser 2013-04-10 16:57 ` Kevin Strasser 2013-05-26 14:38 ` Wim Van Sebroeck 2013-04-13 20:38 ` [PATCH 1/4] mfd: Kontron PLD mfd driver Thomas Gleixner 2013-04-18 4:19 ` Guenter Roeck 2013-04-18 4:40 ` Joe Perches 2013-04-18 4:40 ` Joe Perches 2013-04-18 13:35 ` Guenter Roeck 2013-04-18 13:35 ` Guenter Roeck 2013-04-18 16:42 ` Joe Perches 2013-04-18 18:40 ` Guenter Roeck 2013-06-18 21:04 ` [PATCH v2 0/4] Kontron PLD drivers Kevin Strasser 2013-06-18 21:04 ` Kevin Strasser 2013-06-18 21:04 ` [PATCH v2 1/4] mfd: Kontron PLD mfd driver Kevin Strasser 2013-06-19 8:40 ` Linus Walleij 2013-06-19 9:11 ` Samuel Ortiz 2013-06-19 9:48 ` Mark Brown 2013-06-19 9:12 ` Thomas Gleixner 2013-06-19 18:03 ` Kevin Strasser 2013-06-19 20:35 ` Guenter Roeck 2013-06-18 21:04 ` [PATCH v2 2/4] i2c: Kontron PLD i2c bus driver Kevin Strasser 2013-06-18 21:04 ` [PATCH v2 3/4] gpio: Kontron PLD gpio driver Kevin Strasser 2013-06-19 8:36 ` Linus Walleij 2013-06-27 22:14 ` Kevin Strasser 2013-06-18 21:04 ` [PATCH v2 4/4] watchdog: Kontron PLD watchdog timer driver Kevin Strasser 2013-06-24 4:00 ` [PATCH v3 0/4] Kontron PLD drivers Kevin Strasser 2013-06-24 4:00 ` Kevin Strasser 2013-06-24 4:00 ` [PATCH v3 1/4] mfd: Kontron PLD mfd driver Kevin Strasser 2013-06-24 4:00 ` [PATCH v3 2/4] i2c: Kontron PLD i2c bus driver Kevin Strasser 2013-07-01 6:40 ` Wolfram Sang 2013-07-01 6:40 ` Wolfram Sang 2013-06-24 4:00 ` [PATCH v3 3/4] gpio: Kontron PLD gpio driver Kevin Strasser 2013-07-21 14:31 ` Linus Walleij 2013-06-24 4:00 ` [PATCH v3 4/4] watchdog: Kontron PLD watchdog timer driver Kevin Strasser 2013-06-27 18:23 ` Kevin Strasser 2013-06-27 21:47 ` Samuel Ortiz 2013-06-27 21:47 ` Samuel Ortiz 2013-06-27 22:05 ` Kevin Strasser 2013-06-27 22:05 ` Kevin Strasser 2013-06-24 12:06 ` [PATCH v3 0/4] Kontron PLD drivers Samuel Ortiz 2013-06-24 12:06 ` Samuel Ortiz 2013-06-24 16:09 ` Wolfram Sang 2013-06-24 16:09 ` Wolfram Sang 2013-06-27 20:34 ` Wim Van Sebroeck 2013-06-27 20:34 ` Wim Van Sebroeck 2013-06-27 21:48 ` Samuel Ortiz 2013-06-27 21:48 ` Samuel Ortiz
Reply instructions: You may reply publicly to this message via plain-text email using any one of the following methods: * Save the following mbox file, import it into your mail client, and reply-to-all from there: mbox Avoid top-posting and favor interleaved quoting: https://en.wikipedia.org/wiki/Posting_style#Interleaved_style * Reply using the --to, --cc, and --in-reply-to switches of git-send-email(1): git send-email \ --in-reply-to=1365441321-21952-3-git-send-email-kevin.strasser@linux.intel.com \ --to=kevin.strasser@linux.intel.com \ --cc=ben-linux@fluff.org \ --cc=dvhart@linux.intel.com \ --cc=grant.likely@secretlab.ca \ --cc=gregkh@linuxfoundation.org \ --cc=linus.walleij@linaro.org \ --cc=linux-i2c@vger.kernel.org \ --cc=linux-kernel@vger.kernel.org \ --cc=linux-watchdog@vger.kernel.org \ --cc=mibru@gmx.de \ --cc=michael.brunner@kontron.com \ --cc=sameo@linux.intel.com \ --cc=wim@iguana.be \ --cc=wsa@the-dreams.de \ /path/to/YOUR_REPLY https://kernel.org/pub/software/scm/git/docs/git-send-email.html * If your mail client supports setting the In-Reply-To header via mailto: links, try the mailto: linkBe sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.