Enable buttons GPIO passthrough

Message ID 959CAFA1E282D14FB901BE9A7BF4E7724E41D8EE@shsmsx102.ccr.corp.intel.com
State Not Applicable, archived
Headers show
Series
  • Enable buttons GPIO passthrough
Related show

Commit Message

Wang, Kuiying Dec. 11, 2018, 8:02 a.m.
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.

--
2.16.2

Thanks,
Kwin.

Comments

Joel Stanley Dec. 13, 2018, 1:21 a.m. | #1
On Tue, 11 Dec 2018 at 18:32, Wang, Kuiying <kuiying.wang@intel.com> wrote:
>
> 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.

I think that this direction would be better than a misc driver. I've
added Linus, the maintainer for these subsystems, and the linux-gpio
mailing list to cc.

Cheers,

Joel

>
> 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@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.
>
>
Wang, Kuiying Dec. 24, 2018, 2:56 a.m. | #2
Hi Linus,
Who could help and work w/ me on this?


Thanks,
Kwin.

-----Original Message-----
From: Joel Stanley [mailto:joel@jms.id.au] 
Sent: Thursday, December 13, 2018 9:21 AM
To: Wang, Kuiying <kuiying.wang@intel.com>; Linus Walleij <linus.walleij@linaro.org>; linux-gpio@vger.kernel.org; Andrew Jeffery <andrew@aj.id.au>
Cc: Andrew Geissler <geissonator@gmail.com>; OpenBMC Maillist <openbmc@lists.ozlabs.org>; Mauery, Vernon <vernon.mauery@intel.com>; Feist, James <james.feist@intel.com>; Yoo, Jae Hyun <jae.hyun.yoo@intel.com>
Subject: Re: Enable buttons GPIO passthrough

On Tue, 11 Dec 2018 at 18:32, Wang, Kuiying <kuiying.wang@intel.com> wrote:
>
> 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.

I think that this direction would be better than a misc driver. I've added Linus, the maintainer for these subsystems, and the linux-gpio mailing list to cc.

Cheers,

Joel

>
> 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@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.
>
>
Linus Walleij Jan. 11, 2019, 9:01 a.m. | #3
On Thu, Dec 13, 2018 at 2:21 AM Joel Stanley <joel@jms.id.au> wrote:
> On Tue, 11 Dec 2018 at 18:32, Wang, Kuiying <kuiying.wang@intel.com> wrote:
> >
> > 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.
>
> I think that this direction would be better than a misc driver. I've
> added Linus, the maintainer for these subsystems, and the linux-gpio
> mailing list to cc.

I am sorry but I'm confused about this. It's a very terse explanation
of the problem and a bunch of code.

Can you folks please define what exactly you mean with a
"GPIO passthrough" and what you are trying to achieve with
this on these buttons?

Yours,
Linus Walleij
Andrew Jeffery Jan. 14, 2019, 2:59 a.m. | #4
On Fri, 11 Jan 2019, at 19:31, Linus Walleij wrote:
> On Thu, Dec 13, 2018 at 2:21 AM Joel Stanley <joel@jms.id.au> wrote:
> > On Tue, 11 Dec 2018 at 18:32, Wang, Kuiying <kuiying.wang@intel.com> wrote:
> > >
> > > 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.
> >
> > I think that this direction would be better than a misc driver. I've
> > added Linus, the maintainer for these subsystems, and the linux-gpio
> > mailing list to cc.
> 
> I am sorry but I'm confused about this. It's a very terse explanation
> of the problem and a bunch of code.
> 
> Can you folks please define what exactly you mean with a
> "GPIO passthrough" and what you are trying to achieve with
> this on these buttons?

On the Aspeed BMC SoCs, several GPIO banks (D and E) can be
configured so the pins in the low half of a bank of 8 pins are
input, and the pins in the high half of the bank are output. The
line state of pins 0 <= i < 4 is reflected on pin i + 4. This way it
appears as though the BMC is not present in the circuit.

This is useful when the BMC may not have firmware of its own,
or if it does, if the BMC firmware is not required on the critical
path of the host's boot process. If or when the BMC firmware
boots up, it may choose to break the pass-through behaviour
in order to provide its own out-of-band management capabilities.

This can be used for things like a box's power button as
mentioned: If the BMC is not up, poke the host directly,
otherwise, poke the BMC firmware and have it poke the host.

The pass-through settings are tied to the SoC's pinmux. Intuitively
it should be integrated into the pinmux driver, but IIRC there was
some ugly coupling of the function that didn't have a neat solution
with the currently-separate GPIO and pinmux drivers.

Hope that helps.

Andrew
Linus Walleij Jan. 14, 2019, 8:19 a.m. | #5
On Mon, Jan 14, 2019 at 3:59 AM Andrew Jeffery <andrew@aj.id.au> wrote:
> On Fri, 11 Jan 2019, at 19:31, Linus Walleij wrote:

> > I am sorry but I'm confused about this. It's a very terse explanation
> > of the problem and a bunch of code.
> >
> > Can you folks please define what exactly you mean with a
> > "GPIO passthrough" and what you are trying to achieve with
> > this on these buttons?
>
> On the Aspeed BMC SoCs, several GPIO banks (D and E) can be
> configured so the pins in the low half of a bank of 8 pins are
> input, and the pins in the high half of the bank are output. The
> line state of pins 0 <= i < 4 is reflected on pin i + 4. This way it
> appears as though the BMC is not present in the circuit.
>
> This is useful when the BMC may not have firmware of its own,
> or if it does, if the BMC firmware is not required on the critical
> path of the host's boot process. If or when the BMC firmware
> boots up, it may choose to break the pass-through behaviour
> in order to provide its own out-of-band management capabilities.
>
> This can be used for things like a box's power button as
> mentioned: If the BMC is not up, poke the host directly,
> otherwise, poke the BMC firmware and have it poke the host.
>
> The pass-through settings are tied to the SoC's pinmux. Intuitively
> it should be integrated into the pinmux driver, but IIRC there was
> some ugly coupling of the function that didn't have a neat solution
> with the currently-separate GPIO and pinmux drivers.
>
> Hope that helps.

Yes I get it now! Thanks a lot!

That's a very neat hardware trick, I like how they think.

Pin config in the pin control driver is indeed the recommended
way to go, if possible.

If not possible, so that this particular trick needs to be handled
on the GPIO side of the world, I would recommend to work
with adding it as generic GPIO line config on the GPIO side.

That means, document passthrough in
include/linux/pinctrl/pinconf-generic.h
Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt
so it is a generic config in the pin control world, then
also make it a GPIO-only binding:

Documentation/devicetree/bindings/gpio/gpio.txt
include/dt-bindings/gpio/gpio.h
And parse it as an argument for the second cell in the
GPIO phandle, then manage it in the GPIO driver
.set_config() callback without calling into the pin config
backend.

Thomas Petazzoni is currently looking into using some
of the standard pin config set ups on the pure GPIO
path so keep him in CC. I think it will be necessary to go
down this path for some line configurations rather than
forcibly keep GPIO and pin control electronic settings
separate.

My idea as maintainer is that I want to keep
the available configs together so that it can be seamlessly
switched between the GPIO front-end and a pin control
back-end.

Yours,
Linus Walleij
Wang, Kuiying Jan. 15, 2019, 7:21 a.m. | #6
Hi Linus,
Thanks a lot for your sharing and suggestion.
My proposal to enable buttons GPIO pass-through based on pinmux driver as following:
1. remove codes about SCU7C clearance (from line 194 to line 236) in aspeed_sig_expr_set function in pinctrl-aspeed.c file.
2. create 2 new functions in pinctrl-aspeed.c file: “aspeed_gpio_passthrough_enable” and “aspeed_gpio_passthrough_disable”
3. bind button gpio to pinctrl

Please feel free to correct me and share your preferred solution.

Thanks,
Kwin.


-----Original Message-----
From: Linus Walleij [mailto:linus.walleij@linaro.org] 
Sent: Monday, January 14, 2019 4:19 PM
To: Andrew Jeffery <andrew@aj.id.au>; Thomas Petazzoni <thomas.petazzoni@bootlin.com>
Cc: Joel Stanley <joel@jms.id.au>; Wang, Kuiying <kuiying.wang@intel.com>; open list:GPIO SUBSYSTEM <linux-gpio@vger.kernel.org>; Andrew Geissler <geissonator@gmail.com>; OpenBMC Maillist <openbmc@lists.ozlabs.org>; Mauery, Vernon <vernon.mauery@intel.com>; Feist, James <james.feist@intel.com>; Yoo, Jae Hyun <jae.hyun.yoo@intel.com>
Subject: Re: Enable buttons GPIO passthrough

On Mon, Jan 14, 2019 at 3:59 AM Andrew Jeffery <andrew@aj.id.au> wrote:
> On Fri, 11 Jan 2019, at 19:31, Linus Walleij wrote:

> > I am sorry but I'm confused about this. It's a very terse 
> > explanation of the problem and a bunch of code.
> >
> > Can you folks please define what exactly you mean with a "GPIO 
> > passthrough" and what you are trying to achieve with this on these 
> > buttons?
>
> On the Aspeed BMC SoCs, several GPIO banks (D and E) can be configured 
> so the pins in the low half of a bank of 8 pins are input, and the 
> pins in the high half of the bank are output. The line state of pins 0 
> <= i < 4 is reflected on pin i + 4. This way it appears as though the 
> BMC is not present in the circuit.
>
> This is useful when the BMC may not have firmware of its own, or if it 
> does, if the BMC firmware is not required on the critical path of the 
> host's boot process. If or when the BMC firmware boots up, it may 
> choose to break the pass-through behaviour in order to provide its own 
> out-of-band management capabilities.
>
> This can be used for things like a box's power button as
> mentioned: If the BMC is not up, poke the host directly, otherwise, 
> poke the BMC firmware and have it poke the host.
>
> The pass-through settings are tied to the SoC's pinmux. Intuitively it 
> should be integrated into the pinmux driver, but IIRC there was some 
> ugly coupling of the function that didn't have a neat solution with 
> the currently-separate GPIO and pinmux drivers.
>
> Hope that helps.

Yes I get it now! Thanks a lot!

That's a very neat hardware trick, I like how they think.

Pin config in the pin control driver is indeed the recommended way to go, if possible.

If not possible, so that this particular trick needs to be handled on the GPIO side of the world, I would recommend to work with adding it as generic GPIO line config on the GPIO side.

That means, document passthrough in
include/linux/pinctrl/pinconf-generic.h
Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt
so it is a generic config in the pin control world, then also make it a GPIO-only binding:

Documentation/devicetree/bindings/gpio/gpio.txt
include/dt-bindings/gpio/gpio.h
And parse it as an argument for the second cell in the GPIO phandle, then manage it in the GPIO driver
.set_config() callback without calling into the pin config backend.

Thomas Petazzoni is currently looking into using some of the standard pin config set ups on the pure GPIO path so keep him in CC. I think it will be necessary to go down this path for some line configurations rather than forcibly keep GPIO and pin control electronic settings separate.

My idea as maintainer is that I want to keep the available configs together so that it can be seamlessly switched between the GPIO front-end and a pin control back-end.

Yours,
Linus Walleij
Wang, Kuiying Jan. 15, 2019, 9:52 a.m. | #7
Hi Linus,
Based on pinmux to do passthrough is too complex and no reference, I have another 2 simple proposals to do button GPIO passthrough as following:
1. extend "passthrough" to the "direction" property of gpio, use "value" to control it be disabled/enabled.
     This solution needs: 
	Add "passthrough" parsing in gpiolib-sysfs.c
	Add " direction_passthrough" supported in gpio_chip struct in include/linux/gpio/driver.h
	Add "aspeed_gpio_dir_passthrough" function in gpio-aspeed.c file to map "passthrough" direction.
	Modify aspeed_gpio_set function in gpio-aspeed.c file to modify register according to the "value" to disable/enable pass-through.
2. extend "value" property of gpio to support the third value "2", which means pass-through is enabled, otherwise passthrough is disabled.
    This solution is very simple to support pass-through, just need:
	Modify aspeed_gpio_set function in gpio-aspeed.c file. When value is set to "2" enable passthrough via modify corresponding register, otherwise disable passthrough.
		
Do you agree to use one simple solution to do passthrough?

Thanks,
Kwin.


-----Original Message-----
From: Wang, Kuiying 
Sent: Tuesday, January 15, 2019 3:21 PM
To: 'Linus Walleij' <linus.walleij@linaro.org>; Andrew Jeffery <andrew@aj.id.au>; Thomas Petazzoni <thomas.petazzoni@bootlin.com>
Cc: Joel Stanley <joel@jms.id.au>; open list:GPIO SUBSYSTEM <linux-gpio@vger.kernel.org>; Andrew Geissler <geissonator@gmail.com>; OpenBMC Maillist <openbmc@lists.ozlabs.org>; Mauery, Vernon <vernon.mauery@intel.com>; Feist, James <james.feist@intel.com>; Yoo, Jae Hyun <jae.hyun.yoo@intel.com>
Subject: RE: Enable buttons GPIO passthrough

Hi Linus,
Thanks a lot for your sharing and suggestion.
My proposal to enable buttons GPIO pass-through based on pinmux driver as following:
1. remove codes about SCU7C clearance (from line 194 to line 236) in aspeed_sig_expr_set function in pinctrl-aspeed.c file.
2. create 2 new functions in pinctrl-aspeed.c file: “aspeed_gpio_passthrough_enable” and “aspeed_gpio_passthrough_disable”
3. bind button gpio to pinctrl

Please feel free to correct me and share your preferred solution.

Thanks,
Kwin.


-----Original Message-----
From: Linus Walleij [mailto:linus.walleij@linaro.org]
Sent: Monday, January 14, 2019 4:19 PM
To: Andrew Jeffery <andrew@aj.id.au>; Thomas Petazzoni <thomas.petazzoni@bootlin.com>
Cc: Joel Stanley <joel@jms.id.au>; Wang, Kuiying <kuiying.wang@intel.com>; open list:GPIO SUBSYSTEM <linux-gpio@vger.kernel.org>; Andrew Geissler <geissonator@gmail.com>; OpenBMC Maillist <openbmc@lists.ozlabs.org>; Mauery, Vernon <vernon.mauery@intel.com>; Feist, James <james.feist@intel.com>; Yoo, Jae Hyun <jae.hyun.yoo@intel.com>
Subject: Re: Enable buttons GPIO passthrough

On Mon, Jan 14, 2019 at 3:59 AM Andrew Jeffery <andrew@aj.id.au> wrote:
> On Fri, 11 Jan 2019, at 19:31, Linus Walleij wrote:

> > I am sorry but I'm confused about this. It's a very terse 
> > explanation of the problem and a bunch of code.
> >
> > Can you folks please define what exactly you mean with a "GPIO 
> > passthrough" and what you are trying to achieve with this on these 
> > buttons?
>
> On the Aspeed BMC SoCs, several GPIO banks (D and E) can be configured 
> so the pins in the low half of a bank of 8 pins are input, and the 
> pins in the high half of the bank are output. The line state of pins 0 
> <= i < 4 is reflected on pin i + 4. This way it appears as though the 
> BMC is not present in the circuit.
>
> This is useful when the BMC may not have firmware of its own, or if it 
> does, if the BMC firmware is not required on the critical path of the 
> host's boot process. If or when the BMC firmware boots up, it may 
> choose to break the pass-through behaviour in order to provide its own 
> out-of-band management capabilities.
>
> This can be used for things like a box's power button as
> mentioned: If the BMC is not up, poke the host directly, otherwise, 
> poke the BMC firmware and have it poke the host.
>
> The pass-through settings are tied to the SoC's pinmux. Intuitively it 
> should be integrated into the pinmux driver, but IIRC there was some 
> ugly coupling of the function that didn't have a neat solution with 
> the currently-separate GPIO and pinmux drivers.
>
> Hope that helps.

Yes I get it now! Thanks a lot!

That's a very neat hardware trick, I like how they think.

Pin config in the pin control driver is indeed the recommended way to go, if possible.

If not possible, so that this particular trick needs to be handled on the GPIO side of the world, I would recommend to work with adding it as generic GPIO line config on the GPIO side.

That means, document passthrough in
include/linux/pinctrl/pinconf-generic.h
Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt
so it is a generic config in the pin control world, then also make it a GPIO-only binding:

Documentation/devicetree/bindings/gpio/gpio.txt
include/dt-bindings/gpio/gpio.h
And parse it as an argument for the second cell in the GPIO phandle, then manage it in the GPIO driver
.set_config() callback without calling into the pin config backend.

Thomas Petazzoni is currently looking into using some of the standard pin config set ups on the pure GPIO path so keep him in CC. I think it will be necessary to go down this path for some line configurations rather than forcibly keep GPIO and pin control electronic settings separate.

My idea as maintainer is that I want to keep the available configs together so that it can be seamlessly switched between the GPIO front-end and a pin control back-end.

Yours,
Linus Walleij
Linus Walleij Jan. 15, 2019, 12:04 p.m. | #8
Hi Kwin!

On Tue, Jan 15, 2019 at 8:21 AM Wang, Kuiying <kuiying.wang@intel.com> wrote:

> My proposal to enable buttons GPIO pass-through based on pinmux driver as following:
> 1. remove codes about SCU7C clearance (from line 194 to line 236) in
> aspeed_sig_expr_set function in pinctrl-aspeed.c file.
> 2. create 2 new functions in pinctrl-aspeed.c file: “aspeed_gpio_passthrough_enable”
> and “aspeed_gpio_passthrough_disable”

These are coding implementation issues that you need to discuss with
the maintainer of pinctrl-aspeed.c, Andrew.

> 3. bind button gpio to pinctrl

This will just be a pin control state like "default" or any other stare.

What you need to do first is establish a standard for this, so augment the
pin control bindings and the generic parser code to handle this and
provide a new enumerator back to .set_config in the pin config
portion of the pin control driver.

So:

Step 1: standardize pin pass through for everyone
Step 2: implement support for this in the Aspeed driver

I think using a name like "gpio-passthrough" is misleading, something
describing what is actually happening, like "latch" or just "passthrough"
is better.

Yours,
Linus Walleij
Linus Walleij Jan. 15, 2019, 12:09 p.m. | #9
Hi Kwin,

> Based on pinmux to do passthrough is too complex and no reference,

Don't let that discourage you! We are some of the world's most important
software engineers in the kernel, and we don't just do the simple stuff,
like filling in the blanks guided by previous example, we do the really
complicated stuff of inventing the frameworks.

> I have another 2 simple proposals to do button GPIO passthrough as following:
> 1. extend "passthrough" to the "direction" property of gpio, use "value" to
> control it be disabled/enabled.

This is what I described in my first reply. A .set_config() option.
But it still needs to patch the DT bindings for generic pin config
and add a globally uniquie enumerator for this config to
pinconf-generic.h since GPIO is reusing these generics,
so begin with that in any case.

> 2. extend "value" property of gpio to support the third value "2",
> which means pass-through is enabled, otherwise passthrough is disabled.

No, sorry, this is the wrong idea.

.set_value() sets the GPIO line as high or low, it is essentially
boolean and it is an integer only for historical reasons.

.set_config() is what we use to set up debounce or open drain
and such electronic configurations on GPIO lines, and this
should be done there, unless Andrew prefers that it is gets
done in the pin control driver.

Yours,
Linus Walleij
Wang, Kuiying Jan. 16, 2019, 2:30 p.m. | #10
Hi Joel,
Do agree to use proposal #1? (extend "passthrough" to the "direction" property of gpio, use  "value" to control it be disabled/enabled.)

All,
Any other comments?

Thanks,
Kwin.


-----Original Message-----
From: Linus Walleij [mailto:linus.walleij@linaro.org] 
Sent: Tuesday, January 15, 2019 8:10 PM
To: Wang, Kuiying <kuiying.wang@intel.com>
Cc: Andrew Jeffery <andrew@aj.id.au>; Thomas Petazzoni <thomas.petazzoni@bootlin.com>; Joel Stanley <joel@jms.id.au>; open list:GPIO SUBSYSTEM <linux-gpio@vger.kernel.org>; Andrew Geissler <geissonator@gmail.com>; OpenBMC Maillist <openbmc@lists.ozlabs.org>; Mauery, Vernon <vernon.mauery@intel.com>; Feist, James <james.feist@intel.com>; Yoo, Jae Hyun <jae.hyun.yoo@intel.com>
Subject: Re: Enable buttons GPIO passthrough

Hi Kwin,

> Based on pinmux to do passthrough is too complex and no reference,

Don't let that discourage you! We are some of the world's most important software engineers in the kernel, and we don't just do the simple stuff, like filling in the blanks guided by previous example, we do the really complicated stuff of inventing the frameworks.

> I have another 2 simple proposals to do button GPIO passthrough as following:
> 1. extend "passthrough" to the "direction" property of gpio, use 
> "value" to control it be disabled/enabled.

This is what I described in my first reply. A .set_config() option.
But it still needs to patch the DT bindings for generic pin config and add a globally uniquie enumerator for this config to pinconf-generic.h since GPIO is reusing these generics, so begin with that in any case.

> 2. extend "value" property of gpio to support the third value "2", 
> which means pass-through is enabled, otherwise passthrough is disabled.

No, sorry, this is the wrong idea.

.set_value() sets the GPIO line as high or low, it is essentially boolean and it is an integer only for historical reasons.

.set_config() is what we use to set up debounce or open drain and such electronic configurations on GPIO lines, and this should be done there, unless Andrew prefers that it is gets done in the pin control driver.

Yours,
Linus Walleij
Linus Walleij Jan. 21, 2019, 1:09 p.m. | #11
Hi Kwin,

On Wed, Jan 16, 2019 at 3:30 PM Wang, Kuiying <kuiying.wang@intel.com> wrote:

> Hi Joel,
> Do agree to use proposal #1? (extend "passthrough" to the "direction" property of gpio, use  "value" to control it be disabled/enabled.)
>
> All,
> Any other comments?

I think you maybe misunderstood my previous mail, or I misunderstood yours :)

Make sure that passthrough mode is set up in the .set_config() callback,
nothing else.

Do not change the signatures of the direction functions or set_value()
functions in gpiolib.

But maybe you are talking about implementation details in the specific
GPIO driver for Aspeed? Then I just missed that part, sorry.

Yours,
Linus Walleij
Wang, Kuiying Jan. 22, 2019, 10:39 a.m. | #12
Hi Linus,
Let me attach a draft patch, it will be easy to understand.

if someone wanna enable passthrough just need to override like " gpio->chip.direction_passthrough = aspeed_gpio_dir_passthrough;"
else passthrough is disabled.

In app level, just need to echo "passthrough" to enable passthrough like "echo passthrough > /sys/class/gpio/gpio35/direction"

diff --git a/drivers/gpio/gpio-aspeed.c b/drivers/gpio/gpio-aspeed.c
index 2342e154029b..1091ceded76a 100644
--- a/drivers/gpio/gpio-aspeed.c
+++ b/drivers/gpio/gpio-aspeed.c
@@ -482,6 +482,33 @@ static int aspeed_gpio_dir_out(struct gpio_chip *gc,
 	return 0;
 }
 
+static int aspeed_gpio_dir_passthrough(struct gpio_chip *gc,
+			       unsigned int offset)
+{
+	struct aspeed_gpio *gpio = gpiochip_get_data(gc);
+	const struct aspeed_gpio_bank *bank = to_bank(offset);
+	void __iomem *addr = bank_reg(gpio, bank, reg_dir);
+	unsigned long flags;
+	bool copro;
+	u32 reg;
+	printk("kwin::aspeed_gpio_dir_passthrough");
+	//TODO enable_passthrouhg();
+	spin_lock_irqsave(&gpio->lock, flags);
+
+	reg = ioread32(addr);
+	reg |= GPIO_BIT(offset);
+
+	copro = aspeed_gpio_copro_request(gpio, offset);
+	//__aspeed_gpio_set(gc, offset, val);
+	iowrite32(reg, addr);
+
+	if (copro)
+		aspeed_gpio_copro_release(gpio, offset);
+	spin_unlock_irqrestore(&gpio->lock, flags);
+
+	return 0;
+}
+
 static int aspeed_gpio_get_direction(struct gpio_chip *gc, unsigned int offset)
 {
 	struct aspeed_gpio *gpio = gpiochip_get_data(gc);
@@ -1188,6 +1215,7 @@ static int __init aspeed_gpio_probe(struct platform_device *pdev)
 	gpio->chip.parent = &pdev->dev;
 	gpio->chip.direction_input = aspeed_gpio_dir_in;
 	gpio->chip.direction_output = aspeed_gpio_dir_out;
+	gpio->chip.direction_passthrough = aspeed_gpio_dir_passthrough;
 	gpio->chip.get_direction = aspeed_gpio_get_direction;
 	gpio->chip.request = aspeed_gpio_request;
 	gpio->chip.free = aspeed_gpio_free;
diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c
index 3dbaf489a8a5..5c78067ad250 100644
--- a/drivers/gpio/gpiolib-sysfs.c
+++ b/drivers/gpio/gpiolib-sysfs.c
@@ -89,6 +89,8 @@ static ssize_t direction_store(struct device *dev,
 		status = gpiod_direction_output_raw(desc, 0);
 	else if (sysfs_streq(buf, "in"))
 		status = gpiod_direction_input(desc);
+	else if (sysfs_streq(buf, "passthrough"))
+		status = gpiod_direction_passthrough(desc);
 	else
 		status = -EINVAL;
 
@@ -300,6 +302,8 @@ static ssize_t edge_store(struct device *dev,
 }
 static DEVICE_ATTR_RW(edge);
 
+
+
 /* Caller holds gpiod-data mutex. */
 static int gpio_sysfs_set_active_low(struct device *dev, int value)
 {
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index a8e01d99919c..530573bd8711 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -2536,6 +2536,33 @@ int gpiod_direction_input(struct gpio_desc *desc)
 }
 EXPORT_SYMBOL_GPL(gpiod_direction_input);
 
+
+int gpiod_direction_passthrough(struct gpio_desc *desc)
+{
+	struct gpio_chip	*chip;
+	int			status = -EINVAL;
+
+	VALIDATE_DESC(desc);
+	chip = desc->gdev->chip;
+
+	if (!chip->direction_passthrough) {
+		gpiod_warn(desc,
+			"%s: missing direction_passthrough() operations\n",
+			__func__);
+		return -EIO;
+	}
+
+	status = chip->direction_passthrough(chip, gpio_chip_hwgpio(desc));
+	if (status == 0)
+		clear_bit(FLAG_IS_OUT, &desc->flags);
+
+	trace_gpio_direction(desc_to_gpio(desc), 1, status);
+
+	return status;
+}
+EXPORT_SYMBOL_GPL(gpiod_direction_passthrough);
+
+
 static int gpio_set_drive_single_ended(struct gpio_chip *gc, unsigned offset,
 				       enum pin_config_param mode)
 {
diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h
index a4d5eb37744a..07e30f9e3bce 100644
--- a/include/linux/gpio/driver.h
+++ b/include/linux/gpio/driver.h
@@ -245,6 +245,8 @@ struct gpio_chip {
 						unsigned offset);
 	int			(*direction_output)(struct gpio_chip *chip,
 						unsigned offset, int value);
+	int			(*direction_passthrough)(struct gpio_chip *chip,
+						unsigned offset);
 	int			(*get)(struct gpio_chip *chip,
 						unsigned offset);
 	int			(*get_multiple)(struct gpio_chip *chip,


Thanks,
Kwin.


-----Original Message-----
From: Linus Walleij [mailto:linus.walleij@linaro.org] 
Sent: Monday, January 21, 2019 9:10 PM
To: Wang, Kuiying <kuiying.wang@intel.com>
Cc: Joel Stanley <joel@jms.id.au>; Andrew Jeffery <andrew@aj.id.au>; Thomas Petazzoni <thomas.petazzoni@bootlin.com>; open list:GPIO SUBSYSTEM <linux-gpio@vger.kernel.org>; Andrew Geissler <geissonator@gmail.com>; OpenBMC Maillist <openbmc@lists.ozlabs.org>; Mauery, Vernon <vernon.mauery@intel.com>; Feist, James <james.feist@intel.com>; Yoo, Jae Hyun <jae.hyun.yoo@intel.com>; Nguyen, Hai V <hai.v.nguyen@intel.com>; Khetan, Sharad <sharad.khetan@intel.com>
Subject: Re: Enable buttons GPIO passthrough

Hi Kwin,

On Wed, Jan 16, 2019 at 3:30 PM Wang, Kuiying <kuiying.wang@intel.com> wrote:

> Hi Joel,
> Do agree to use proposal #1? (extend "passthrough" to the "direction" 
> property of gpio, use  "value" to control it be disabled/enabled.)
>
> All,
> Any other comments?

I think you maybe misunderstood my previous mail, or I misunderstood yours :)

Make sure that passthrough mode is set up in the .set_config() callback, nothing else.

Do not change the signatures of the direction functions or set_value() functions in gpiolib.

But maybe you are talking about implementation details in the specific GPIO driver for Aspeed? Then I just missed that part, sorry.

Yours,
Linus Walleij

Patch

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@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