From patchwork Tue Dec 11 08:02:47 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Wang, Kuiying" X-Patchwork-Id: 1010878 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.ozlabs.org (lists.ozlabs.org [203.11.71.2]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 43DXVz6dq8z9s4s for ; Tue, 11 Dec 2018 19:03:07 +1100 (AEDT) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=intel.com Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) by lists.ozlabs.org (Postfix) with ESMTP id 43DXVz4wGvzDqk8 for ; Tue, 11 Dec 2018 19:03:07 +1100 (AEDT) Authentication-Results: lists.ozlabs.org; dmarc=pass (p=none dis=none) header.from=intel.com X-Original-To: openbmc@lists.ozlabs.org Delivered-To: openbmc@lists.ozlabs.org Authentication-Results: lists.ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=intel.com (client-ip=192.55.52.93; helo=mga11.intel.com; envelope-from=kuiying.wang@intel.com; receiver=) Authentication-Results: lists.ozlabs.org; dmarc=pass (p=none dis=none) header.from=intel.com Received: from mga11.intel.com (mga11.intel.com [192.55.52.93]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by lists.ozlabs.org (Postfix) with ESMTPS id 43DXVn3llQzDqfb for ; Tue, 11 Dec 2018 19:02:53 +1100 (AEDT) X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from fmsmga005.fm.intel.com ([10.253.24.32]) by fmsmga102.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 11 Dec 2018 00:02:51 -0800 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.56,341,1539673200"; d="scan'208,217";a="302813901" Received: from fmsmsx103.amr.corp.intel.com ([10.18.124.201]) by fmsmga005.fm.intel.com with ESMTP; 11 Dec 2018 00:02:51 -0800 Received: from fmsmsx120.amr.corp.intel.com (10.18.124.208) by FMSMSX103.amr.corp.intel.com (10.18.124.201) with Microsoft SMTP Server (TLS) id 14.3.408.0; Tue, 11 Dec 2018 00:02:50 -0800 Received: from shsmsx152.ccr.corp.intel.com (10.239.6.52) by fmsmsx120.amr.corp.intel.com (10.18.124.208) with Microsoft SMTP Server (TLS) id 14.3.408.0; Tue, 11 Dec 2018 00:02:49 -0800 Received: from shsmsx102.ccr.corp.intel.com ([169.254.2.182]) by SHSMSX152.ccr.corp.intel.com ([169.254.6.222]) with mapi id 14.03.0415.000; Tue, 11 Dec 2018 16:02:48 +0800 From: "Wang, Kuiying" To: Joel Stanley , Andrew Geissler , Andrew Jeffery Subject: Enable buttons GPIO passthrough Thread-Topic: Enable buttons GPIO passthrough Thread-Index: AdSRJmQpdKkHGMIOQneqw0Mzic5pdA== Date: Tue, 11 Dec 2018 08:02:47 +0000 Message-ID: <959CAFA1E282D14FB901BE9A7BF4E7724E41D8EE@shsmsx102.ccr.corp.intel.com> Accept-Language: en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: x-originating-ip: [10.239.127.40] MIME-Version: 1.0 X-BeenThere: openbmc@lists.ozlabs.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Development list for OpenBMC List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: OpenBMC Maillist , "Feist, James" , "Yoo, Jae Hyun" , "Mauery, Vernon" Errors-To: openbmc-bounces+incoming=patchwork.ozlabs.org@lists.ozlabs.org Sender: "openbmc" 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. 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 "); +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 +#include +#include +#include +#include +#include +#include + +#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