All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 2/6] Add Advantech iManager GPIO driver
@ 2016-01-10  9:11 richard.dorsch
  0 siblings, 0 replies; only message in thread
From: richard.dorsch @ 2016-01-10  9:11 UTC (permalink / raw)
  To: linux-kernel
  Cc: lm-sensors, linux-i2c, linux-watchdog, linux-gpio, lee.jones,
	jdelvare, linux, wim, jo.sunga, Richard Vidal-Dorsch

From: Richard Vidal-Dorsch <richard.dorsch@gmail.com>

Signed-off-by: Richard Vidal-Dorsch <richard.dorsch@gmail.com>
---
 drivers/gpio/Kconfig              |   8 ++
 drivers/gpio/Makefile             |   2 +
 drivers/gpio/imanager-ec-gpio.c   |  98 +++++++++++++++++++++
 drivers/gpio/imanager-gpio.c      | 181 ++++++++++++++++++++++++++++++++++++++
 include/linux/mfd/imanager/gpio.h |  27 ++++++
 5 files changed, 316 insertions(+)
 create mode 100644 drivers/gpio/imanager-ec-gpio.c
 create mode 100644 drivers/gpio/imanager-gpio.c
 create mode 100644 include/linux/mfd/imanager/gpio.h

diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index b18bea0..0f80947 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -765,6 +765,14 @@ config GPIO_DLN2
 	  This driver can also be built as a module. If so, the module
 	  will be called gpio-dln2.
 
+config GPIO_IMANAGER
+	tristate "Advantech iManager GPIO support"
+	depends on MFD_IMANAGER
+	help
+	  Say yes here to support Advantech iManager GPIO functionality
+	  of some Advantech SOM, MIO, AIMB, and PCM modules/boards.
+	  Requires mfd-core and imanager-core to function properly.
+
 config GPIO_JANZ_TTL
 	tristate "Janz VMOD-TTL Digital IO Module"
 	depends on MFD_JANZ_CMODIO
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 986dbd8..0df55e4 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -41,6 +41,8 @@ obj-$(CONFIG_GPIO_F7188X)	+= gpio-f7188x.o
 obj-$(CONFIG_GPIO_GE_FPGA)	+= gpio-ge.o
 obj-$(CONFIG_GPIO_GRGPIO)	+= gpio-grgpio.o
 obj-$(CONFIG_GPIO_ICH)		+= gpio-ich.o
+gpio-imanager-objs		:= imanager-gpio.o imanager-ec-gpio.o
+obj-$(CONFIG_GPIO_IMANAGER)	+= gpio-imanager.o
 obj-$(CONFIG_GPIO_IOP)		+= gpio-iop.o
 obj-$(CONFIG_GPIO_IT87)		+= gpio-it87.o
 obj-$(CONFIG_GPIO_JANZ_TTL)	+= gpio-janz-ttl.o
diff --git a/drivers/gpio/imanager-ec-gpio.c b/drivers/gpio/imanager-ec-gpio.c
new file mode 100644
index 0000000..c448666
--- /dev/null
+++ b/drivers/gpio/imanager-ec-gpio.c
@@ -0,0 +1,98 @@
+/*
+ * Advantech iManager GPIO core
+ *
+ * Copyright (C) 2016 Advantech Co., Ltd., Irvine, CA, USA
+ * Author: Richard Vidal-Dorsch <richard.dorsch@advantech.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.
+ */
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/bug.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/byteorder/generic.h>
+#include <linux/mfd/imanager/ec.h>
+#include <linux/mfd/imanager/gpio.h>
+
+#define EC_GPIOF_DIR_OUT	(1 << 6)
+#define EC_GPIOF_DIR_IN		(1 << 7)
+#define EC_GPIOF_LOW		(0 << 0)
+#define EC_GPIOF_HIGH		(1 << 0)
+
+/*
+ * Power-on default:
+ * GPIO[7..4] := Input
+ * GPIO[3..0] := Output
+ */
+
+static const struct imanager_gpio_device *gpio;
+
+int gpio_core_get_state(u32 num)
+{
+	int ret;
+
+	if (WARN_ON(num >= gpio->num))
+		return -EINVAL;
+
+	ret = imanager_read_byte(EC_CMD_HWP_RD, gpio->attr[num].did);
+	if (ret < 0)
+		pr_err("Failed to get GPIO pin state (%x)\n", num);
+
+	return ret;
+}
+
+int gpio_core_set_state(u32 num, bool state)
+{
+	int ret;
+
+	if (WARN_ON(num >= gpio->num))
+		return -EINVAL;
+
+	ret = imanager_write_byte(EC_CMD_HWP_WR, gpio->attr[num].did,
+			    state ? EC_GPIOF_HIGH : EC_GPIOF_LOW);
+	if (ret) {
+		pr_err("Failed to set GPIO pin state (%x)\n", num);
+		return ret;
+	}
+
+	return 0;
+}
+
+int gpio_core_set_direction(u32 num, int dir)
+{
+	int ret;
+
+	if (WARN_ON(num >= gpio->num))
+		return -EINVAL;
+
+	ret = imanager_write_byte(EC_CMD_GPIO_DIR_WR, gpio->attr[num].did,
+			    dir ? EC_GPIOF_DIR_IN : EC_GPIOF_DIR_OUT);
+	if (ret) {
+		pr_err("Failed to set GPIO direction (%x, '%s')\n", num,
+			dir == GPIOF_DIR_OUT ? "OUT" : "IN");
+		return ret;
+	}
+
+	return 0;
+}
+
+int gpio_core_get_max_count(void)
+{
+	return gpio->num;
+}
+
+int gpio_core_init(void)
+{
+	gpio = imanager_get_gpio_device();
+	if (!gpio)
+		return -ENODEV;
+
+	return 0;
+}
+
diff --git a/drivers/gpio/imanager-gpio.c b/drivers/gpio/imanager-gpio.c
new file mode 100644
index 0000000..d4a2b30
--- /dev/null
+++ b/drivers/gpio/imanager-gpio.c
@@ -0,0 +1,181 @@
+/*
+ * Advantech iManager GPIO driver
+ *
+ * Copyright (C) 2016 Advantech Co., Ltd., Irvine, CA, USA
+ * Author: Richard Vidal-Dorsch <richard.dorsch@advantech.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.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/mfd/imanager/core.h>
+#include <linux/mfd/imanager/gpio.h>
+
+struct imanager_gpio_data {
+	struct imanager_device_data *idev;
+	struct gpio_chip chip;
+};
+
+static inline struct imanager_gpio_data *
+to_imanager_gpio_data(struct gpio_chip *chip)
+{
+	return container_of(chip, struct imanager_gpio_data, chip);
+}
+
+static int imanager_direction_in(struct gpio_chip *chip, u32 gpio_num)
+{
+	struct imanager_gpio_data *data = to_imanager_gpio_data(chip);
+	int ret;
+
+	mutex_lock(&data->idev->lock);
+
+	ret = gpio_core_set_direction(gpio_num, GPIOF_DIR_IN);
+	if (ret) {
+		dev_err(chip->dev, "Failed to set direction to 'in' (%d)\n",
+			gpio_num);
+		ret = -EIO;
+	}
+
+	mutex_unlock(&data->idev->lock);
+
+	return ret;
+}
+
+static int
+imanager_direction_out(struct gpio_chip *chip, u32 gpio_num, int val)
+{
+	struct imanager_gpio_data *data = to_imanager_gpio_data(chip);
+	int ret;
+
+	mutex_lock(&data->idev->lock);
+
+	ret = gpio_core_set_direction(gpio_num, GPIOF_DIR_OUT);
+	if (ret) {
+		dev_err(chip->dev, "Failed to set direction to 'out' (%d)\n",
+			gpio_num);
+		ret = -EIO;
+	}
+
+	mutex_unlock(&data->idev->lock);
+
+	return ret;
+}
+
+static int imanager_get(struct gpio_chip *chip, u32 gpio_num)
+{
+	struct imanager_gpio_data *data = to_imanager_gpio_data(chip);
+	int ret;
+
+	mutex_lock(&data->idev->lock);
+
+	ret = gpio_core_get_state(gpio_num);
+	if (ret < 0) {
+		dev_err(chip->dev, "Failed to get status (%d)\n", gpio_num);
+		ret = -EIO;
+	}
+
+	mutex_unlock(&data->idev->lock);
+
+	return ret;
+}
+
+static void imanager_set(struct gpio_chip *chip, u32 gpio_num,
+			 int val)
+{
+	struct imanager_gpio_data *data = to_imanager_gpio_data(chip);
+	int ret;
+
+	mutex_lock(&data->idev->lock);
+
+	ret = gpio_core_set_state(gpio_num, val);
+	if (ret < 0)
+		dev_err(chip->dev, "Failed to set status (%d)\n", gpio_num);
+
+	mutex_unlock(&data->idev->lock);
+}
+
+static int imanager_gpio_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct imanager_device_data *idev = dev_get_drvdata(dev->parent);
+	struct imanager_gpio_data *data;
+	struct gpio_chip *chip;
+	int ret;
+
+	if (!idev) {
+		dev_err(dev, "Invalid platform data\n");
+		return -EINVAL;
+	}
+
+	ret = gpio_core_init();
+	if (ret) {
+		dev_err(dev, "Failed initializing GPIO core\n");
+		return ret;
+	}
+
+	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	data->idev = idev;
+
+	platform_set_drvdata(pdev, data);
+
+	chip = &data->chip;
+
+	chip->owner	= THIS_MODULE;
+	chip->dev	= dev;
+	chip->label	= "imanager_gpio";
+
+	chip->base	= -1;
+	chip->ngpio	= gpio_core_get_max_count();
+
+	chip->get	= imanager_get;
+	chip->set	= imanager_set;
+
+	chip->can_sleep	= 1;
+
+	chip->direction_input  = imanager_direction_in;
+	chip->direction_output = imanager_direction_out;
+
+	ret = gpiochip_add(chip);
+	if (ret < 0) {
+		dev_err(dev, "Failed to register driver\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int imanager_remove(struct platform_device *pdev)
+{
+	struct imanager_gpio_data *data = platform_get_drvdata(pdev);
+
+	gpiochip_remove(&data->chip);
+
+	return 0;
+}
+
+static struct platform_driver imanager_gpio_driver = {
+	.driver = {
+		.name	= "imanager_gpio",
+	},
+	.probe	= imanager_gpio_probe,
+	.remove	= imanager_remove,
+};
+
+module_platform_driver(imanager_gpio_driver);
+
+MODULE_DESCRIPTION("Advantech iManager GPIO Driver");
+MODULE_AUTHOR("Richard Vidal-Dorsch <richard.dorsch at advantech.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:imanager_gpio");
diff --git a/include/linux/mfd/imanager/gpio.h b/include/linux/mfd/imanager/gpio.h
new file mode 100644
index 0000000..dfc849f
--- /dev/null
+++ b/include/linux/mfd/imanager/gpio.h
@@ -0,0 +1,27 @@
+/*
+ * Advantech iManager GPIO core
+ *
+ * Copyright (C) 2016 Advantech Co., Ltd., Irvine, CA, USA
+ * Author: Richard Vidal-Dorsch <richard.dorsch@advantech.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.
+ */
+
+#ifndef __GPIO_H__
+#define __GPIO_H__
+
+#include <linux/gpio.h>
+#include <linux/types.h>
+
+int gpio_core_init(void);
+
+int gpio_core_get_max_count(void);
+
+int gpio_core_get_state(u32 num);
+int gpio_core_set_state(u32 num, bool state);
+int gpio_core_set_direction(u32 num, int dir);
+
+#endif
-- 
2.6.4

^ permalink raw reply related	[flat|nested] only message in thread

only message in thread, other threads:[~2016-01-10  9:11 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-01-10  9:11 [PATCH v2 2/6] Add Advantech iManager GPIO driver richard.dorsch

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.