Hi Linus, Who could help and work w/ me on this? Thanks, Kwin. -----Original Message----- From: Joel Stanley [mailto:joel@xxxxxxxxx] Sent: Thursday, December 13, 2018 9:21 AM To: Wang, Kuiying <kuiying.wang@xxxxxxxxx>; Linus Walleij <linus.walleij@xxxxxxxxxx>; linux-gpio@xxxxxxxxxxxxxxx; Andrew Jeffery <andrew@xxxxxxxx> Cc: Andrew Geissler <geissonator@xxxxxxxxx>; OpenBMC Maillist <openbmc@xxxxxxxxxxxxxxxx>; Mauery, Vernon <vernon.mauery@xxxxxxxxx>; Feist, James <james.feist@xxxxxxxxx>; Yoo, Jae Hyun <jae.hyun.yoo@xxxxxxxxx> Subject: Re: Enable buttons GPIO passthrough On Tue, 11 Dec 2018 at 18:32, Wang, Kuiying <kuiying.wang@xxxxxxxxx> 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@xxxxxxxxx>"); > > +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. > >