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