Enable buttons GPIO passthrough

Joel Stanley joel at jms.id.au
Thu Dec 13 12:21:20 AEDT 2018


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


More information about the openbmc mailing list