UBUNTU: ODM: gpio: add driver for AAEON devices
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>
This commit is contained in:
committed by
Paolo Pisati
parent
0cf5311691
commit
93d1e6cb39
@@ -1586,6 +1586,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
|
||||
|
||||
@@ -24,6 +24,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_AGGREGATOR) += gpio-aggregator.o
|
||||
|
||||
@@ -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");
|
||||
Reference in New Issue
Block a user