]> git.proxmox.com Git - mirror_ubuntu-impish-kernel.git/commitdiff
UBUNTU: ODM: gpio: add driver for AAEON devices
authorKunyang_Fan <kunyang_fan@asus.com>
Wed, 16 Jun 2021 05:56:59 +0000 (13:56 +0800)
committerAndrea Righi <andrea.righi@canonical.com>
Mon, 28 Jun 2021 06:04:07 +0000 (08:04 +0200)
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>
Acked-by: Stefan Bader <stefan.bader@canonical.com>
Acked-by: Kleber Sacilotto de Souza <kleber.souza@canonical.com>
Signed-off-by: Andrea Righi <andrea.righi@canonical.com>
drivers/gpio/Kconfig
drivers/gpio/Makefile
drivers/gpio/gpio-aaeon.c [new file with mode: 0644]

index 3c69b785cb79d465ac2ff70350a30e4db5f4edad..f20978f6b38a27bc36eecf73c9960ab3fbf022d2 100644 (file)
@@ -1448,6 +1448,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
index d7c81e1611a4d5d5d2f09a9b90e3aa6c4a86f0a9..6553ab07da60a962ddd93fa332836ab7a1c5004b 100644 (file)
@@ -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 (file)
index 0000000..3183c45
--- /dev/null
@@ -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");