diff mbox series

[2/6,SRU,G] UBUNTU: ODM: gpio: add driver for AAEON devices

Message ID 20210525101720.522132-3-acelan.kao@canonical.com
State New
Headers show
Series Add support for IO functions of AAEON devices | expand

Commit Message

AceLan Kao May 25, 2021, 10:17 a.m. UTC
From: Kunyang_Fan <kunyang_fan@asus.com>

BugLink: https://bugs.launchpad.net/bugs/1929504

This patch add support for the GPIO pins whose control are
transported to BIOS through ASUS WMI interface.

Signed-off-by: Kunyang_Fan <kunyang_fan@asus.com>
Review-by: Kai-Heng Feng <kai.heng.feng@canonical.com>
Review-by: Chia-Lin Kao (AceLan) <acelan.kao@canonical.com>
Signed-off-by: Chia-Lin Kao (AceLan) <acelan.kao@canonical.com>
---
 drivers/gpio/Kconfig      |  11 ++
 drivers/gpio/Makefile     |   1 +
 drivers/gpio/gpio-aaeon.c | 205 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 217 insertions(+)
 create mode 100644 drivers/gpio/gpio-aaeon.c
diff mbox series

Patch

diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 6d2437676a75..26acac425583 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -1356,6 +1356,18 @@  endmenu
 menu "PCI GPIO expanders"
 	depends on PCI
 
+config GPIO_AAEON
+	tristate "AAEON GPIO support"
+	depends on ASUS_WMI
+	depends on UBUNTU_ODM_DRIVERS
+	select MFD_AAEON
+	help
+	  Say yes here to support GPIO pins on Single Board Computers produced
+	  by AAEON.
+
+	  This driver leverages the ASUS WMI interface to access device
+	  resources.
+
 config GPIO_AMD8111
 	tristate "AMD 8111 GPIO driver"
 	depends on X86 || COMPILE_TEST
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 1e4894e0bf0f..50682c5c49d3 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -23,6 +23,7 @@  obj-$(CONFIG_GPIO_104_IDI_48)		+= gpio-104-idi-48.o
 obj-$(CONFIG_GPIO_104_IDIO_16)		+= gpio-104-idio-16.o
 obj-$(CONFIG_GPIO_74X164)		+= gpio-74x164.o
 obj-$(CONFIG_GPIO_74XX_MMIO)		+= gpio-74xx-mmio.o
+obj-$(CONFIG_GPIO_AAEON)                += gpio-aaeon.o
 obj-$(CONFIG_GPIO_ADNP)			+= gpio-adnp.o
 obj-$(CONFIG_GPIO_ADP5520)		+= gpio-adp5520.o
 obj-$(CONFIG_GPIO_ADP5588)		+= gpio-adp5588.o
diff --git a/drivers/gpio/gpio-aaeon.c b/drivers/gpio/gpio-aaeon.c
new file mode 100644
index 000000000000..3183c45dd194
--- /dev/null
+++ b/drivers/gpio/gpio-aaeon.c
@@ -0,0 +1,205 @@ 
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * AAEON GPIO driver
+ * Copyright (c) 2021, AAEON Ltd.
+ *
+ * Author: Edward Lin <edward1_lin@aaeon.com.tw>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#include <linux/acpi.h>
+#include <linux/bitops.h>
+#include <linux/gpio/driver.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_data/x86/asus-wmi.h>
+#include <linux/platform_device.h>
+
+#define DRVNAME "gpio_aaeon"
+#define ASUS_NB_WMI_EVENT_GUID   "0B3CBB35-E3C2-45ED-91C2-4C5A6D195D1C"
+#define AAEON_WMI_MGMT_GUID      "97845ED0-4E6D-11DE-8A39-0800200C9A66"
+
+#define GET_GPIO_NUMBER_ID       0x00010000
+#define GET_LEVEL_METHOD_ID      0x00010001
+#define SET_LEVEL_METHOD_ID      0x00010002
+#define GET_DIRECTION_METHOD_ID  0x00010003
+#define SET_DIRECTION_METHOD_ID  0x00010004
+#define GET_SIO_NUMBER_METHOD_ID 0xF0010
+
+struct aaeon_gpio_bank {
+	struct gpio_chip chip;
+	unsigned int regbase;
+	struct aaeon_gpio_data *data;
+};
+
+struct aaeon_gpio_data {
+	int nr_bank;
+	struct aaeon_gpio_bank *bank;
+};
+
+static int aaeon_gpio_get_number(void);
+static int aaeon_gpio_get_direction(struct gpio_chip *chip,
+				 unsigned int offset);
+static int aaeon_gpio_output_set_direction(struct gpio_chip *chip,
+				 unsigned int offset, int value);
+static int aaeon_gpio_input_set_direction(struct gpio_chip *chip,
+				 unsigned int offset);
+static int aaeon_gpio_get(struct gpio_chip *chip,
+				 unsigned int offset);
+static void aaeon_gpio_set(struct gpio_chip *chip, unsigned int offset,
+				 int value);
+
+#define AAEON_GPIO_BANK(_base, _ngpio, _regbase)			\
+{									\
+	.chip = {							\
+		.label            = DRVNAME,				\
+		.owner            = THIS_MODULE,			\
+		.get_direction    = aaeon_gpio_get_direction,		\
+		.direction_input  = aaeon_gpio_input_set_direction,     \
+		.direction_output = aaeon_gpio_output_set_direction,    \
+		.get              = aaeon_gpio_get,			\
+		.set              = aaeon_gpio_set,			\
+		.base             = _base,				\
+		.ngpio            = _ngpio,				\
+		.can_sleep        = true,				\
+	},								\
+	.regbase = _regbase,						\
+}
+
+static struct aaeon_gpio_bank aaeon_gpio_bank[] = {
+	AAEON_GPIO_BANK(0, 0, 0xF0),
+};
+
+static int aaeon_gpio_get_direction(struct gpio_chip *chip, unsigned int offset)
+{
+	int err, retval;
+	u32 dev_id = 0x0;
+
+	dev_id |= offset;
+	err = asus_wmi_evaluate_method(GET_DIRECTION_METHOD_ID, dev_id,
+				       0, &retval);
+	if (err)
+		return err;
+
+	return retval;
+}
+
+static int aaeon_gpio_input_set_direction(struct gpio_chip *chip,
+					  unsigned int offset)
+{
+	int err, retval;
+	u32 dev_id;
+
+	dev_id = BIT(16) | offset;
+	err = asus_wmi_evaluate_method(SET_DIRECTION_METHOD_ID, dev_id,
+				       0, &retval);
+	if (err)
+		return err;
+
+	return retval;
+}
+
+static int aaeon_gpio_output_set_direction(struct gpio_chip *chip,
+					   unsigned int offset, int value)
+{
+	int err, retval;
+	u32 dev_id = 0x0;
+
+	dev_id |= offset;
+	err = asus_wmi_evaluate_method(SET_DIRECTION_METHOD_ID, dev_id,
+				       0, &retval);
+	if (err)
+		return err;
+
+	return retval;
+}
+
+static int aaeon_gpio_get(struct gpio_chip *chip, unsigned int offset)
+{
+	int err, retval;
+	u32 dev_id = 0x0;
+
+	dev_id |= offset;
+	err = asus_wmi_evaluate_method(GET_LEVEL_METHOD_ID, dev_id, 0, &retval);
+	if (err)
+		return err;
+
+	return retval;
+}
+
+static void aaeon_gpio_set(struct gpio_chip *chip, unsigned int offset,
+			   int value)
+{
+	int retval;
+	u32 dev_id = offset;
+
+	if (value)
+		dev_id = BIT(16) | dev_id;
+
+	asus_wmi_evaluate_method(SET_LEVEL_METHOD_ID, dev_id, 0, &retval);
+}
+
+static int aaeon_gpio_get_number(void)
+{
+	int err, retval;
+
+	err = asus_wmi_evaluate_method(GET_GPIO_NUMBER_ID,
+				       GET_SIO_NUMBER_METHOD_ID,
+				       0, &retval);
+	if (err)
+		return err;
+
+	return retval;
+}
+
+static int __init aaeon_gpio_probe(struct platform_device *pdev)
+{
+	int err, i;
+	int dio_number = 0;
+	struct aaeon_gpio_data *data;
+	struct aaeon_gpio_bank *bank;
+
+	/* Prevent other drivers adding this platfom device */
+	if (!wmi_has_guid(AAEON_WMI_MGMT_GUID)) {
+		pr_debug("AAEON Management GUID not found\n");
+		return -ENODEV;
+	}
+
+	dio_number = aaeon_gpio_get_number();
+	if (dio_number < 0)
+		return -ENODEV;
+
+	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	data->nr_bank = ARRAY_SIZE(aaeon_gpio_bank);
+	data->bank = aaeon_gpio_bank;
+	platform_set_drvdata(pdev, data);
+	bank = &data->bank[0];
+	bank->chip.parent = &pdev->dev;
+	bank->chip.ngpio = dio_number;
+	bank->data = data;
+	err = devm_gpiochip_add_data(&pdev->dev, &bank->chip, bank);
+	if (err)
+		pr_debug("Failed to register gpiochip %d: %d\n", i, err);
+
+	return err;
+}
+
+static struct platform_driver aaeon_gpio_driver = {
+	.driver = {
+		.name = "gpio-aaeon",
+	},
+};
+
+module_platform_driver_probe(aaeon_gpio_driver, aaeon_gpio_probe);
+
+MODULE_ALIAS("platform:gpio-aaeon");
+MODULE_DESCRIPTION("AAEON GPIO Driver");
+MODULE_AUTHOR("Edward Lin <edward1_lin@aaeon.com.tw>");
+MODULE_LICENSE("GPL v2");