blob: e33d03a854661f16c13794acee027aca94c69695 [file] [log] [blame]
/*
* Copyright (c) 2023 Intel Corporation.
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <errno.h>
#include <zephyr/acpi/acpi.h>
#include "soc.h"
#include "soc_gpio.h"
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(soc_gpio, CONFIG_SOC_LOG_LEVEL);
#define GET_GPIO_BASE_NUM ("\\_SB.GINF")
static int gpio_info_acpi_get(uint8_t uid, uint32_t bank_idx, uint32_t field_idx,
uint32_t *ret_val, bool ginf)
{
int status;
ACPI_OBJECT args[3];
ACPI_OBJECT_LIST arg_list;
ACPI_OBJECT gpio_obj;
arg_list.Pointer = args;
if (ginf) {
arg_list.Count = 3;
args[0].Type = ACPI_TYPE_INTEGER;
/* u-id is used to indicate the order of communities in ACPI table,
* in GINF method with u-id parameter community 2 is not existing
* but ACPI table stores community 3 group values in uid index 2
* and same for communities 4 and 5. So following logic is used
* while sending u-id to GINF.
*/
args[0].Integer.Value = (uid > GINF_MISSING_UID) ? (--uid) : uid;
args[1].Type = ACPI_TYPE_INTEGER;
args[1].Integer.Value = bank_idx;
args[2].Type = ACPI_TYPE_INTEGER;
args[2].Integer.Value = field_idx;
} else {
arg_list.Count = 2;
args[0].Type = ACPI_TYPE_INTEGER;
args[0].Integer.Value = bank_idx;
args[1].Type = ACPI_TYPE_INTEGER;
args[1].Integer.Value = field_idx;
}
status = acpi_invoke_method(GET_GPIO_BASE_NUM, &arg_list, &gpio_obj);
if (status) {
return status;
}
if (gpio_obj.Type == ACPI_TYPE_INTEGER) {
*ret_val = gpio_obj.Integer.Value;
}
return status;
}
int soc_acpi_gpio_resource_get(int bank_idx, char *hid, char *uid, struct gpio_acpi_res *res,
bool ginf)
{
int ret;
struct acpi_dev *acpi_child;
struct acpi_irq_resource irq_res;
struct acpi_mmio_resource mmio_res;
uint32_t field_val[8] = {0};
uint16_t irqs[CONFIG_ACPI_IRQ_VECTOR_MAX];
struct acpi_reg_base reg_base[CONFIG_ACPI_MMIO_ENTRIES_MAX];
uint8_t uid_int = 0;
acpi_child = acpi_device_get(hid, uid);
if (!acpi_child) {
LOG_ERR("acpi_device_get failed");
return -EIO;
}
mmio_res.mmio_max = ARRAY_SIZE(reg_base);
mmio_res.reg_base = reg_base;
ret = acpi_device_mmio_get(acpi_child, &mmio_res);
if (ret) {
LOG_ERR("acpi_device_mmio_get failed");
return ret;
}
irq_res.irq_vector_max = ARRAY_SIZE(irqs);
irq_res.irqs = irqs;
ret = acpi_device_irq_get(acpi_child, &irq_res);
if (ret) {
LOG_ERR("acpi_device_irq_get failed");
return ret;
}
res->irq = irq_res.irqs[0];
res->irq_flags = irq_res.flags;
if (ACPI_RESOURCE_TYPE_GET(&mmio_res) == ACPI_RES_TYPE_MEM) {
int num_fields = ARRAY_SIZE(field_val);
if (uid) {
uid_int = atoi(uid);
}
for (int i = 0; i < num_fields; i++) {
ret = gpio_info_acpi_get(uid_int, bank_idx, i, &field_val[i], ginf);
if (ret) {
LOG_ERR("gpio_info_acpi_get failed");
return ret;
}
}
if (ginf) {
/* GINF method used to fetch GPIO data from ACPI table requires 3
* parameters for each GPIO group.
* In this method Reg_base stores 64-bit physical address of the GPIO group.
*/
res->reg_base = ACPI_MMIO_GET(&mmio_res);
res->len = ACPI_RESOURCE_SIZE_GET(&mmio_res);
res->num_pins = field_val[0];
res->pad_base = field_val[1];
res->host_owner_reg = field_val[2];
res->pad_owner_reg = field_val[3];
res->gp_evt_stat_reg = field_val[4];
res->base_num = field_val[7];
} else {
/* GINF method used to fetch GPIO data from ACPI table requires 2
* parameters for each GPIO group.
* In this method reg_base stores 32-bit physical address of the GPIO group
*/
res->reg_base = ACPI_MMIO_GET(&mmio_res) & (~0x0FFFFFF);
res->reg_base += field_val[0];
res->len = ACPI_RESOURCE_SIZE_GET(&mmio_res);
res->num_pins = field_val[1];
res->pad_base = field_val[2];
res->host_owner_reg = field_val[3];
res->pad_owner_reg = field_val[4];
res->gp_evt_stat_reg = field_val[5];
res->base_num = field_val[6];
}
} else {
LOG_ERR("ACPI_RES_TYPE_MEM failed");
return -ENODEV;
}
return 0;
}