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