Enable buttons GPIO passthrough

Wang, Kuiying kuiying.wang at intel.com
Tue Dec 11 19:02:47 AEDT 2018


Hi Joel/Andrew,
I write a drive to enable GPIO passthrough for buttons (like power/reset/id button) as following attached patch.
Do you think it is acceptable?
Or we could do it in pinmux and extend gpio driver? Design passthrough state except in/out.
What's your suggestions?

Thanks Kwin.

diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index f2062546250c..e94ee86820d3 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -4,6 +4,12 @@
 menu "Misc devices"
+config GPIO_PASS_THROUGH
+             tristate "GPIO Pass Through"
+             depends on (ARCH_ASPEED || COMPILE_TEST)
+             help
+               Enable this for buttons GPIO pass through.
+
config SENSORS_LIS3LV02D
              tristate
              depends on INPUT
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index bb89694e6b4b..13b8b8edbb70 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -61,3 +61,4 @@ obj-$(CONFIG_ASPEED_LPC_SIO)   += aspeed-lpc-sio.o
obj-$(CONFIG_PCI_ENDPOINT_TEST)      += pci_endpoint_test.o
obj-$(CONFIG_OCXL)                     += ocxl/
obj-$(CONFIG_MISC_RTSX)                         += cardreader/
+obj-$(CONFIG_GPIO_PASS_THROUGH)   += gpio-passthrough.o
diff --git a/drivers/misc/gpio-passthrough.c b/drivers/misc/gpio-passthrough.c
new file mode 100644
index 000000000000..0126fc08ae55
--- /dev/null
+++ b/drivers/misc/gpio-passthrough.c
@@ -0,0 +1,260 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2018 Intel Corporation
+*/
+
+#include "gpio-passthrough.h"
+
+struct aspeed_gpio_pass_through_dev {
+             struct miscdevice              miscdev;
+             unsigned int        addr;
+             unsigned int        size;
+};
+
+static struct aspeed_gpio_pass_through_dev ast_cdev_gpio_pass_through;
+
+static long ast_passthru_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+             long ret = 0;
+             struct passthru_ioctl_data passthru_data;
+
+             if (cmd == GPIO_IOC_PASSTHRU)
+             {
+                            if (copy_from_user(&passthru_data,
+                                           (void __user*)arg, sizeof(passthru_data)))
+                                           return -EFAULT;
+                            if (passthru_data.idx >= GPIO_PASSTHRU_MAX)
+                                           return -EINVAL;
+
+                            switch (passthru_data.cmd) {
+                            case SET_GPIO_PASSTHRU_ENABLE:
+                                           ast_set_passthru_enable(passthru_data.idx,
+                                                                         passthru_data.data);
+                                           break;
+                            case GET_GPIO_PASSTHRU_ENABLE:
+                                           passthru_data.data = ast_get_passthru_enable(passthru_data.idx);
+                                           if (copy_to_user((void __user*)arg, &passthru_data,
+                                                                                                       sizeof(passthru_data)))
+                                           ret = -EFAULT;
+                            break;
+
+                            case SET_GPIO_PASSTHRU_OUT:
+                                           ast_set_passthru_out(passthru_data.idx, passthru_data.data);
+                            break;
+
+                            default:
+                                           ret = -EINVAL;
+                            break;
+                            }
+             }
+             return ret;
+
+}
+
+static int ast_passthru_open(struct inode *inode, struct file *filp)
+{
+             return container_of(filp->private_data,
+                            struct aspeed_gpio_pass_through_dev, miscdev);
+}
+
+static const struct file_operations ast_gpio_pth_fops = {
+             .owner          = THIS_MODULE,
+             .llseek         = no_llseek,
+             .unlocked_ioctl = ast_passthru_ioctl,
+             .open           = ast_passthru_open,
+};
+
+static struct miscdevice ast_gpio_pth_miscdev = {
+             .minor = MISC_DYNAMIC_MINOR,
+             .name = GPIO_PASS_THROUGH_NAME,
+             .fops = &ast_gpio_pth_fops,
+};
+
+static u32 ast_scu_base  = IO_ADDRESS(AST_SCU_BASE);
+
+static inline u32 ast_scu_read(u32 reg)
+{
+             return readl((void *)(ast_scu_base + reg));
+}
+
+static inline void ast_scu_write(u32 val, u32 reg)
+{
+#ifdef CONFIG_AST_SCU_LOCK
+             writel(SCU_PROTECT_UNLOCK, (void *)(ast_scu_base + AST_SCU_PROTECT));
+             writel(val,                (void *)(ast_scu_base + reg));
+             writel(0x000000AA,         (void *)(ast_scu_base + AST_SCU_PROTECT));
+#else
+             writel(SCU_PROTECT_UNLOCK, (void *)(ast_scu_base + AST_SCU_PROTECT));
+             writel(val,                (void *)(ast_scu_base + reg));
+#endif
+}
+
+static int gpio_pass_through_probe(struct platform_device *pdev)
+{
+             struct aspeed_gpio_pass_through_dev   *gpio_pth_dev = &ast_cdev_gpio_pass_through;
+             struct device *dev = &pdev->dev;
+             struct resource *rc;
+
+             dev_set_drvdata(&pdev->dev, gpio_pth_dev);
+             rc = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+             if (!rc) {
+                            dev_err(dev, "Fail to platform_get_resource\n");
+                            return -ENXIO;
+             }
+             gpio_pth_dev->addr = rc->start;
+             gpio_pth_dev->size = resource_size(rc);
+             gpio_pth_dev->miscdev = ast_gpio_pth_miscdev;
+             ast_passthru_init();
+             printk("GPIO PASS THROUGH DRIVER is loaded \n");
+             return misc_register(&gpio_pth_dev->miscdev);
+}
+
+static int gpio_pass_through_remove(struct platform_device *pdev)
+{
+             struct aspeed_gpio_pass_through_dev *gpio_pth_dev =
+                                                          dev_get_drvdata(&pdev->dev);
+             misc_deregister(&gpio_pth_dev->miscdev);
+             printk("GPIO PASS THROUGH DRIVER is removing \n");
+
+             return 0;
+}
+
+static struct platform_driver gpio_pass_through_driver = {
+             .probe     = gpio_pass_through_probe,
+             .remove    = gpio_pass_through_remove,
+             .driver    = {
+                            .name  = "gpio-pass-through",
+        .owner = THIS_MODULE,
+
+             },
+};
+
+/* GPIOE group only */
+struct gpio_passthru {
+             u32 passthru_mask;
+             u16 pin_in;
+             u16 pin_out;
+};
+
+static struct gpio_passthru passthru_settings[GPIO_PASSTHRU_MAX] = {
+             [GPIO_PASSTHRU0] = {
+                                           .passthru_mask = (1 << 12), /* SCU8C[12] */
+                                           .pin_in        = PGPIO_PIN(GPIOE, 0),
+                                           .pin_out       = PGPIO_PIN(GPIOE, 1),
+                            },
+
+             [GPIO_PASSTHRU1] = {
+                                           .passthru_mask = (1 << 13), /* SCU8C[13] */
+                                           .pin_in        = PGPIO_PIN(GPIOE, 2),
+                                           .pin_out       = PGPIO_PIN(GPIOE, 3),
+                            },
+
+             [GPIO_PASSTHRU2] = {
+                                           .passthru_mask = (1 << 14), /* SCU8C[14] */
+                                           .pin_in        = PGPIO_PIN(GPIOE, 4),
+                                           .pin_out       = PGPIO_PIN(GPIOE, 5),
+                            },
+
+             [GPIO_PASSTHRU3] = {
+                                           .passthru_mask = (1 << 15), /* SCU8C[15] */
+                                           .pin_in        = PGPIO_PIN(GPIOE, 6),
+                                           .pin_out       = PGPIO_PIN(GPIOE, 7),
+                            },
+};
+
+static void ast_set_passthru_enable(
+                                           unsigned short idx, unsigned int enable)
+{
+             u32 val;
+             unsigned long flags;
+             struct gpio_passthru *passthru = &passthru_settings[idx];
+
+             local_irq_save(flags);
+
+             val = ast_scu_read(AST_SCU_FUN_PIN_CTRL4);
+             if (enable)
+                            val |=  (passthru->passthru_mask);
+             else
+                            val &= ~(passthru->passthru_mask);
+             ast_scu_write(val, AST_SCU_FUN_PIN_CTRL4);
+
+             local_irq_restore(flags);
+}
+
+static unsigned int ast_get_passthru_enable(unsigned short idx)
+{
+             unsigned int enable;
+             unsigned long flags;
+             struct gpio_passthru *passthru = &passthru_settings[idx];
+
+             local_irq_save(flags);
+
+             enable = (ast_scu_read(AST_SCU_FUN_PIN_CTRL4) & passthru->passthru_mask) != 0 ? 1 : 0;
+
+             local_irq_restore(flags);
+
+             return enable;
+}
+
+static void ast_set_passthru_out(
+                                           unsigned short idx, unsigned int val)
+{
+             unsigned long flags;
+             struct gpio_passthru *passthru = &passthru_settings[idx];
+
+             local_irq_save(flags);
+
+             /* Disable PASSTHRU */
+             val  = ast_scu_read(AST_SCU_FUN_PIN_CTRL4);
+             val &= ~(passthru->passthru_mask);
+             ast_scu_write(val, AST_SCU_FUN_PIN_CTRL4);
+
+             local_irq_restore(flags);
+}
+
+static void ast_passthru_init(void)
+{
+             int i;
+             u32 val;
+             unsigned long flags;
+             struct gpio_passthru *passthru;
+
+             local_irq_save(flags);
+
+             /* 1. Enable GPIOE pin mode, SCU80[16:23] = 00 */
+             ast_scu_write(ast_scu_read(AST_SCU_FUN_PIN_CTRL1) & (~0x00FF0000),
+                           AST_SCU_FUN_PIN_CTRL1);
+
+             /* 2. Enable them by setting SCU8C[12:15] */
+             for (i = 0; i < GPIO_PASSTHRU_MAX; i++) {
+                            passthru = &passthru_settings[i];
+
+                            val  = ast_scu_read(AST_SCU_FUN_PIN_CTRL4);
+                            val |= passthru->passthru_mask;
+                            ast_scu_write(val, AST_SCU_FUN_PIN_CTRL4);
+             }
+
+             /**************************************************************
+             * 3. Disable HWTrap for GPIOE pass-through mode
+             *
+             * Hardware strap register (SCU70) programming method.
+             *   #Write '1' to SCU70 can set the specific bit with value '1'
+             *    Write '0' has no effect.
+             *   #Write '1' to SCU7C can clear the specific bit of SCU70 to
+             *    value '0'. Write '0' has no effect.
+             **************************************************************/
+             if (ast_scu_read(AST_SCU_HW_STRAP1) & (0x1 << 22))
+                            ast_scu_write((0x1 << 22), AST_SCU_REVISION_ID);
+
+             local_irq_restore(flags);
+
+             printk("HW_STRAP1 = 0x%08X\n", ast_scu_read(AST_SCU_HW_STRAP1));
+}
+
+module_platform_driver(gpio_pass_through_driver);
+
+MODULE_AUTHOR("Kuiying Wang <kuiying.wang at intel.com>");
+MODULE_DESCRIPTION("GPIO Pass Through Control Driver for all buttons like Power/Reset/ID button");
+MODULE_LICENSE("GPL v2");
+
diff --git a/drivers/misc/gpio-passthrough.h b/drivers/misc/gpio-passthrough.h
new file mode 100644
index 000000000000..a7274b8ab31e
--- /dev/null
+++ b/drivers/misc/gpio-passthrough.h
@@ -0,0 +1,60 @@
+#ifndef __GPIO_PASS_THROUGH_H__
+#define __GPIO_PASS_THROUGH_H__
+
+#include <linux/kernel.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/mm.h>
+#include <asm/io.h>
+
+#define PGPIO_PIN(PORT, PIN)   (((PORT) << 3) | ((PIN) & 0x07))
+#define GPIOE    4
+#define AST_SCU_BASE                    0x1E6E2000  /* SCU */
+#define AST_SCU_PROTECT                 0x00        /*  protection key register */
+#define SCU_PROTECT_UNLOCK              0x1688A8A8
+#define AST_SCU_FUN_PIN_CTRL4           0x8C        /*  Multi-function Pin Control#4*/
+#define AST_SCU_FUN_PIN_CTRL1           0x80        /*  Multi-function Pin Control#1*/
+#define AST_SCU_HW_STRAP1               0x70        /*  hardware strapping register */
+#define AST_SCU_REVISION_ID             0x7C        /*  Silicon revision ID register */
+#define GPIO_PASS_THROUGH_NAME          "gpiopassthrough"
+#define IO_ADDRESS(x)                   (x)
+
+enum GPIO_PASSTHRU_INDEX {
+             GPIO_PASSTHRU0 = 0,  /* GPIOE0 -> GPIOE1 */
+             GPIO_PASSTHRU1,      /* GPIOE2 -> GPIOE3 */
+             GPIO_PASSTHRU2,      /* GPIOE4 -> GPIOE5 */
+             GPIO_PASSTHRU3,      /* GPIOE6 -> GPIOE7 */
+
+             GPIO_PASSTHRU_MAX
+};
+
+enum GPIO_PASSTHRU_CMD {
+             SET_GPIO_PASSTHRU_ENABLE = 0,
+             GET_GPIO_PASSTHRU_ENABLE,
+             GET_GPIO_PASSTHRU_IN,
+             SET_GPIO_PASSTHRU_OUT, /* !!! The PASSTHRU will be disabled !!! */
+};
+
+struct passthru_ioctl_data {
+             unsigned short cmd;
+             unsigned short idx;
+             unsigned int   data;
+};
+
+static void ast_set_passthru_enable(
+                                           unsigned short idx, unsigned int enable);
+static unsigned int ast_get_passthru_enable(unsigned short idx);
+static void ast_set_passthru_out(
+                                           unsigned short idx, unsigned int val);
+static void ast_passthru_init(void);
+static inline u32 ast_scu_read(u32 reg);
+static inline void ast_scu_write(u32 val, u32 reg);
+
+/* IOCTL */
+#define GPIO_IOC_BASE       'G'
+#define GPIO_IOC_PASSTHRU   _IOWR(GPIO_IOC_BASE, 1, struct passthru_ioctl_data)
+
+
+#endif
--
2.16.2

Thanks,
Kwin.

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.ozlabs.org/pipermail/openbmc/attachments/20181211/008340ff/attachment-0001.html>


More information about the openbmc mailing list