mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-11-10 15:14:43 +00:00
- New timer API to allow delays with a 32-bit microsecond timer - Add dynamic ACPI structs (DSDT/SSDT) generations to the DM core - x86: Enable ACPI table generation by default - x86: Enable the copy framebuffer on Coral - x86: A few fixes to FSP2 with ApolloLake - x86: Drop setup_pcat_compatibility() - x86: Primary-to-Sideband Bus minor fixes
This commit is contained in:
commit
7c3cc6f106
107 changed files with 7997 additions and 255 deletions
|
@ -190,6 +190,7 @@ config X86
|
|||
imply PCH
|
||||
imply RTC_MC146818
|
||||
imply IRQ
|
||||
imply ACPIGEN if !QEMU
|
||||
|
||||
# Thing to enable for when SPL/TPL are enabled: SPL
|
||||
imply SPL_DM
|
||||
|
|
|
@ -111,7 +111,9 @@
|
|||
uint-value = <(-1234)>;
|
||||
int64-value = /bits/ 64 <0x1111222233334444>;
|
||||
int-array = <5678 9123 4567>;
|
||||
str-value = "test string";
|
||||
interrupts-extended = <&irq 3 0>;
|
||||
acpi,name = "GHIJ";
|
||||
};
|
||||
|
||||
junk {
|
||||
|
@ -253,12 +255,19 @@
|
|||
compatible = "denx,u-boot-devres-test";
|
||||
};
|
||||
|
||||
acpi-test {
|
||||
acpi_test1: acpi-test {
|
||||
compatible = "denx,u-boot-acpi-test";
|
||||
acpi-ssdt-test-data = "ab";
|
||||
acpi-dsdt-test-data = "hi";
|
||||
child {
|
||||
compatible = "denx,u-boot-acpi-test";
|
||||
};
|
||||
};
|
||||
|
||||
acpi-test2 {
|
||||
acpi_test2: acpi-test2 {
|
||||
compatible = "denx,u-boot-acpi-test";
|
||||
acpi-ssdt-test-data = "cd";
|
||||
acpi-dsdt-test-data = "jk";
|
||||
};
|
||||
|
||||
clocks {
|
||||
|
@ -918,6 +927,7 @@
|
|||
setting = "sunrise ohoka";
|
||||
other-node = "/some-bus/c-test@5";
|
||||
int-values = <0x1937 72993>;
|
||||
u-boot,acpi-ssdt-order = <&acpi_test2 &acpi_test1>;
|
||||
chosen-test {
|
||||
compatible = "denx,u-boot-fdt-test";
|
||||
reg = <9 1>;
|
||||
|
|
|
@ -717,6 +717,7 @@ config HAVE_ITSS
|
|||
|
||||
config HAVE_P2SB
|
||||
bool "Enable P2SB"
|
||||
depends on P2SB
|
||||
help
|
||||
Select this to include the driver for the Primary to
|
||||
Sideband Bridge (P2SB) which is found on several Intel
|
||||
|
|
|
@ -8,7 +8,10 @@
|
|||
#include <dm.h>
|
||||
#include <irq.h>
|
||||
#include <log.h>
|
||||
#include <acpi/acpi_device.h>
|
||||
#include <asm/io.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
#include <dt-bindings/interrupt-controller/x86-irq.h>
|
||||
|
||||
/**
|
||||
* struct acpi_gpe_priv - private driver information
|
||||
|
@ -62,13 +65,36 @@ static int acpi_gpe_ofdata_to_platdata(struct udevice *dev)
|
|||
static int acpi_gpe_of_xlate(struct irq *irq, struct ofnode_phandle_args *args)
|
||||
{
|
||||
irq->id = args->args[0];
|
||||
irq->flags = args->args[1];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if CONFIG_IS_ENABLED(ACPIGEN)
|
||||
static int acpi_gpe_get_acpi(const struct irq *irq, struct acpi_irq *acpi_irq)
|
||||
{
|
||||
memset(acpi_irq, '\0', sizeof(*acpi_irq));
|
||||
acpi_irq->pin = irq->id;
|
||||
acpi_irq->mode = irq->flags & IRQ_TYPE_EDGE_BOTH ?
|
||||
ACPI_IRQ_EDGE_TRIGGERED : ACPI_IRQ_LEVEL_TRIGGERED;
|
||||
acpi_irq->polarity = irq->flags &
|
||||
(IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_LEVEL_LOW) ?
|
||||
ACPI_IRQ_ACTIVE_LOW : ACPI_IRQ_ACTIVE_HIGH;
|
||||
acpi_irq->shared = irq->flags & X86_IRQ_TYPE_SHARED ?
|
||||
ACPI_IRQ_SHARED : ACPI_IRQ_EXCLUSIVE;
|
||||
acpi_irq->wake = irq->flags & X86_IRQ_TYPE_WAKE ? ACPI_IRQ_WAKE :
|
||||
ACPI_IRQ_NO_WAKE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct irq_ops acpi_gpe_ops = {
|
||||
.read_and_clear = acpi_gpe_read_and_clear,
|
||||
.of_xlate = acpi_gpe_of_xlate,
|
||||
#if CONFIG_IS_ENABLED(ACPIGEN)
|
||||
.get_acpi = acpi_gpe_get_acpi,
|
||||
#endif
|
||||
};
|
||||
|
||||
static const struct udevice_id acpi_gpe_ids[] = {
|
||||
|
|
|
@ -247,12 +247,13 @@ static int arch_cpu_init_spl(void)
|
|||
ret = pmc_init(pmc);
|
||||
if (ret < 0)
|
||||
return log_msg_ret("Could not init PMC", ret);
|
||||
#ifdef CONFIG_HAVE_ACPI_RESUME
|
||||
ret = pmc_prev_sleep_state(pmc);
|
||||
if (ret < 0)
|
||||
return log_msg_ret("Could not get PMC sleep state", ret);
|
||||
gd->arch.prev_sleep_state = ret;
|
||||
#endif
|
||||
if (IS_ENABLED(CONFIG_HAVE_ACPI_RESUME)) {
|
||||
ret = pmc_prev_sleep_state(pmc);
|
||||
if (ret < 0)
|
||||
return log_msg_ret("Could not get PMC sleep state",
|
||||
ret);
|
||||
gd->arch.prev_sleep_state = ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -16,19 +16,29 @@ int fspm_update_config(struct udevice *dev, struct fspm_upd *upd)
|
|||
{
|
||||
struct fsp_m_config *cfg = &upd->config;
|
||||
struct fspm_arch_upd *arch = &upd->arch;
|
||||
int cache_ret = 0;
|
||||
ofnode node;
|
||||
int ret;
|
||||
|
||||
arch->nvs_buffer_ptr = NULL;
|
||||
prepare_mrc_cache(upd);
|
||||
arch->stack_base = (void *)0xfef96000;
|
||||
cache_ret = prepare_mrc_cache(upd);
|
||||
if (cache_ret && cache_ret != -ENOENT)
|
||||
return log_msg_ret("mrc", cache_ret);
|
||||
arch->stack_base = (void *)(CONFIG_SYS_CAR_ADDR + CONFIG_SYS_CAR_SIZE -
|
||||
arch->stack_size);
|
||||
arch->boot_loader_tolum_size = 0;
|
||||
arch->boot_mode = FSP_BOOT_WITH_FULL_CONFIGURATION;
|
||||
arch->boot_mode = cache_ret ? FSP_BOOT_WITH_FULL_CONFIGURATION :
|
||||
FSP_BOOT_ASSUMING_NO_CONFIGURATION_CHANGES;
|
||||
|
||||
node = dev_ofnode(dev);
|
||||
if (!ofnode_valid(node))
|
||||
return log_msg_ret("fsp-m settings", -ENOENT);
|
||||
|
||||
return fsp_m_update_config_from_dtb(node, cfg);
|
||||
ret = fsp_m_update_config_from_dtb(node, cfg);
|
||||
if (ret)
|
||||
return log_msg_ret("dtb", cache_ret);
|
||||
|
||||
return cache_ret;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include <irq.h>
|
||||
#include <log.h>
|
||||
#include <malloc.h>
|
||||
#include <p2sb.h>
|
||||
#include <acpi/acpi_s3.h>
|
||||
#include <asm/intel_pinctrl.h>
|
||||
#include <asm/io.h>
|
||||
|
@ -21,10 +22,11 @@
|
|||
#include <asm/pci.h>
|
||||
#include <asm/arch/cpu.h>
|
||||
#include <asm/arch/systemagent.h>
|
||||
#include <asm/arch/fsp_bindings.h>
|
||||
#include <asm/arch/fsp/fsp_configs.h>
|
||||
#include <asm/arch/fsp/fsp_s_upd.h>
|
||||
#include <dm/uclass-internal.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <asm/arch/fsp_bindings.h>
|
||||
|
||||
#define PCH_P2SB_E0 0xe0
|
||||
#define HIDE_BIT BIT(0)
|
||||
|
@ -36,29 +38,20 @@ int fsps_update_config(struct udevice *dev, ulong rom_offset,
|
|||
ofnode node;
|
||||
|
||||
if (IS_ENABLED(CONFIG_HAVE_VBT)) {
|
||||
struct binman_entry vbt;
|
||||
void *vbt_buf;
|
||||
void *buf;
|
||||
int ret;
|
||||
|
||||
ret = binman_entry_find("intel-vbt", &vbt);
|
||||
ret = binman_entry_map(ofnode_null(), "intel-vbt", &buf, NULL);
|
||||
if (ret)
|
||||
return log_msg_ret("Cannot find VBT", ret);
|
||||
vbt.image_pos += rom_offset;
|
||||
vbt_buf = malloc(vbt.size);
|
||||
if (!vbt_buf)
|
||||
return log_msg_ret("Alloc VBT", -ENOMEM);
|
||||
if (*(u32 *)buf != VBT_SIGNATURE)
|
||||
return log_msg_ret("VBT signature", -EINVAL);
|
||||
|
||||
/*
|
||||
* Load VBT before devicetree-specific config. This only
|
||||
* supports memory-mapped SPI at present.
|
||||
*/
|
||||
bootstage_start(BOOTSTAGE_ID_ACCUM_MMAP_SPI, "mmap_spi");
|
||||
memcpy(vbt_buf, (void *)vbt.image_pos, vbt.size);
|
||||
bootstage_accum(BOOTSTAGE_ID_ACCUM_MMAP_SPI);
|
||||
if (*(u32 *)vbt_buf != VBT_SIGNATURE)
|
||||
return log_msg_ret("VBT signature", -EINVAL);
|
||||
|
||||
cfg->graphics_config_ptr = (ulong)vbt_buf;
|
||||
cfg->graphics_config_ptr = (ulong)buf;
|
||||
}
|
||||
|
||||
node = dev_read_subnode(dev, "fsp-s");
|
||||
|
@ -68,12 +61,6 @@ int fsps_update_config(struct udevice *dev, ulong rom_offset,
|
|||
return fsp_s_update_config_from_dtb(node, cfg);
|
||||
}
|
||||
|
||||
static void p2sb_set_hide_bit(pci_dev_t dev, int hide)
|
||||
{
|
||||
pci_x86_clrset_config(dev, PCH_P2SB_E0 + 1, HIDE_BIT,
|
||||
hide ? HIDE_BIT : 0, PCI_SIZE_8);
|
||||
}
|
||||
|
||||
/* Configure package power limits */
|
||||
static int set_power_limits(struct udevice *dev)
|
||||
{
|
||||
|
@ -146,15 +133,15 @@ static int set_power_limits(struct udevice *dev)
|
|||
|
||||
int p2sb_unhide(void)
|
||||
{
|
||||
pci_dev_t dev = PCI_BDF(0, 0xd, 0);
|
||||
ulong val;
|
||||
struct udevice *dev;
|
||||
int ret;
|
||||
|
||||
p2sb_set_hide_bit(dev, 0);
|
||||
|
||||
pci_x86_read_config(dev, PCI_VENDOR_ID, &val, PCI_SIZE_16);
|
||||
|
||||
if (val != PCI_VENDOR_ID_INTEL)
|
||||
return log_msg_ret("p2sb unhide", -EIO);
|
||||
ret = uclass_find_first_device(UCLASS_P2SB, &dev);
|
||||
if (ret)
|
||||
return log_msg_ret("p2sb", ret);
|
||||
ret = p2sb_set_hide(dev, false);
|
||||
if (ret)
|
||||
return log_msg_ret("hide", ret);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -173,11 +160,6 @@ int arch_fsps_preinit(void)
|
|||
ret = irq_first_device_type(X86_IRQT_ITSS, &itss);
|
||||
if (ret)
|
||||
return log_msg_ret("no itss", ret);
|
||||
/*
|
||||
* Snapshot the current GPIO IRQ polarities. FSP is setting a default
|
||||
* policy that doesn't honour boards' requirements
|
||||
*/
|
||||
irq_snapshot_polarities(itss);
|
||||
|
||||
/*
|
||||
* Clear the GPI interrupt status and enable registers. These
|
||||
|
@ -192,16 +174,16 @@ int arch_fsps_preinit(void)
|
|||
|
||||
int arch_fsp_init_r(void)
|
||||
{
|
||||
#ifdef CONFIG_HAVE_ACPI_RESUME
|
||||
bool s3wake = gd->arch.prev_sleep_state == ACPI_S3;
|
||||
#else
|
||||
bool s3wake = false;
|
||||
#endif
|
||||
bool s3wake;
|
||||
struct udevice *dev, *itss;
|
||||
int ret;
|
||||
|
||||
if (!ll_boot_init())
|
||||
return 0;
|
||||
|
||||
s3wake = IS_ENABLED(CONFIG_HAVE_ACPI_RESUME) &&
|
||||
gd->arch.prev_sleep_state == ACPI_S3;
|
||||
|
||||
/*
|
||||
* This must be called before any devices are probed. Put any probing
|
||||
* into arch_fsps_preinit() above.
|
||||
|
@ -216,7 +198,11 @@ int arch_fsp_init_r(void)
|
|||
ret = irq_first_device_type(X86_IRQT_ITSS, &itss);
|
||||
if (ret)
|
||||
return log_msg_ret("no itss", ret);
|
||||
/* Restore GPIO IRQ polarities back to previous settings */
|
||||
|
||||
/*
|
||||
* Restore GPIO IRQ polarities back to previous settings. This was
|
||||
* stored in reserve_arch() - see X86_IRQT_ITSS
|
||||
*/
|
||||
irq_restore_polarities(itss);
|
||||
|
||||
/* soc_init() */
|
||||
|
|
|
@ -139,7 +139,7 @@ void acpi_create_fadt(struct acpi_fadt *fadt, struct acpi_facs *facs,
|
|||
header->checksum = table_compute_checksum(fadt, header->length);
|
||||
}
|
||||
|
||||
void acpi_create_gnvs(struct acpi_global_nvs *gnvs)
|
||||
int acpi_create_gnvs(struct acpi_global_nvs *gnvs)
|
||||
{
|
||||
struct udevice *dev;
|
||||
int ret;
|
||||
|
@ -159,9 +159,10 @@ void acpi_create_gnvs(struct acpi_global_nvs *gnvs)
|
|||
gnvs->iuart_en = 1;
|
||||
else
|
||||
gnvs->iuart_en = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_HAVE_ACPI_RESUME
|
||||
/*
|
||||
* The following two routines are called at a very early stage, even before
|
||||
* FSP 2nd phase API fsp_init() is called. Registers off ACPI_BASE_ADDRESS
|
||||
|
@ -204,4 +205,3 @@ void chipset_clear_sleep_state(void)
|
|||
pm1_cnt = inl(ACPI_BASE_ADDRESS + PM1_CNT);
|
||||
outl(pm1_cnt & ~(SLP_TYP), ACPI_BASE_ADDRESS + PM1_CNT);
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -23,11 +23,10 @@ static int prev_sleep_state(struct chipset_power_state *ps)
|
|||
|
||||
if (ps->pm1_sts & WAK_STS) {
|
||||
switch ((ps->pm1_cnt & SLP_TYP) >> SLP_TYP_SHIFT) {
|
||||
#if CONFIG_HAVE_ACPI_RESUME
|
||||
case SLP_TYP_S3:
|
||||
prev_sleep_state = SLEEP_STATE_S3;
|
||||
if (IS_ENABLED(CONFIG_HAVE_ACPI_RESUME))
|
||||
prev_sleep_state = SLEEP_STATE_S3;
|
||||
break;
|
||||
#endif
|
||||
case SLP_TYP_S5:
|
||||
prev_sleep_state = SLEEP_STATE_S5;
|
||||
break;
|
||||
|
|
|
@ -42,7 +42,7 @@ int print_cpuinfo(void)
|
|||
return default_print_cpuinfo();
|
||||
}
|
||||
|
||||
static void board_final_cleanup(void)
|
||||
static void board_final_init(void)
|
||||
{
|
||||
/*
|
||||
* Un-cache the ROM so the kernel has one
|
||||
|
@ -80,7 +80,7 @@ int last_stage_init(void)
|
|||
if (CONFIG_IS_ENABLED(USB_KEYBOARD))
|
||||
usb_init();
|
||||
|
||||
board_final_cleanup();
|
||||
board_final_init();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
#include <net.h>
|
||||
#include <asm/arch/sysinfo.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
/*
|
||||
* This needs to be in the .data section so that it's copied over during
|
||||
* relocation. By default it's put in the .bss section which is simply filled
|
||||
|
@ -243,6 +245,10 @@ int get_coreboot_info(struct sysinfo_t *info)
|
|||
if (addr < 0)
|
||||
return addr;
|
||||
ret = cb_parse_header((void *)addr, 0x1000, info);
|
||||
if (!ret)
|
||||
return -ENOENT;
|
||||
gd->arch.coreboot_table = addr;
|
||||
gd->flags |= GD_FLG_SKIP_LL_INIT;
|
||||
|
||||
return ret == 1 ? 0 : -ENOENT;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include <dm.h>
|
||||
#include <errno.h>
|
||||
#include <init.h>
|
||||
#include <irq.h>
|
||||
#include <log.h>
|
||||
#include <malloc.h>
|
||||
#include <syscon.h>
|
||||
|
@ -163,10 +164,10 @@ int default_print_cpuinfo(void)
|
|||
cpu_has_64bit() ? "x86_64" : "x86",
|
||||
cpu_vendor_name(gd->arch.x86_vendor), gd->arch.x86_device);
|
||||
|
||||
#ifdef CONFIG_HAVE_ACPI_RESUME
|
||||
debug("ACPI previous sleep state: %s\n",
|
||||
acpi_ss_string(gd->arch.prev_sleep_state));
|
||||
#endif
|
||||
if (IS_ENABLED(CONFIG_HAVE_ACPI_RESUME)) {
|
||||
debug("ACPI previous sleep state: %s\n",
|
||||
acpi_ss_string(gd->arch.prev_sleep_state));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -178,10 +179,10 @@ void show_boot_progress(int val)
|
|||
|
||||
#if !defined(CONFIG_SYS_COREBOOT) && !defined(CONFIG_EFI_STUB)
|
||||
/*
|
||||
* Implement a weak default function for boards that optionally
|
||||
* need to clean up the system before jumping to the kernel.
|
||||
* Implement a weak default function for boards that need to do some final init
|
||||
* before the system is ready.
|
||||
*/
|
||||
__weak void board_final_cleanup(void)
|
||||
__weak void board_final_init(void)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -189,14 +190,14 @@ int last_stage_init(void)
|
|||
{
|
||||
struct acpi_fadt __maybe_unused *fadt;
|
||||
|
||||
board_final_cleanup();
|
||||
board_final_init();
|
||||
|
||||
#ifdef CONFIG_HAVE_ACPI_RESUME
|
||||
fadt = acpi_find_fadt();
|
||||
if (IS_ENABLED(CONFIG_HAVE_ACPI_RESUME)) {
|
||||
fadt = acpi_find_fadt();
|
||||
|
||||
if (fadt && gd->arch.prev_sleep_state == ACPI_S3)
|
||||
acpi_resume(fadt);
|
||||
#endif
|
||||
if (fadt && gd->arch.prev_sleep_state == ACPI_S3)
|
||||
acpi_resume(fadt);
|
||||
}
|
||||
|
||||
write_tables();
|
||||
|
||||
|
@ -269,25 +270,36 @@ int cpu_init_r(void)
|
|||
#ifndef CONFIG_EFI_STUB
|
||||
int reserve_arch(void)
|
||||
{
|
||||
#ifdef CONFIG_ENABLE_MRC_CACHE
|
||||
mrccache_reserve();
|
||||
#endif
|
||||
struct udevice *itss;
|
||||
int ret;
|
||||
|
||||
if (IS_ENABLED(CONFIG_ENABLE_MRC_CACHE))
|
||||
mrccache_reserve();
|
||||
|
||||
#ifdef CONFIG_SEABIOS
|
||||
high_table_reserve();
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_HAVE_ACPI_RESUME
|
||||
acpi_s3_reserve();
|
||||
if (IS_ENABLED(CONFIG_HAVE_ACPI_RESUME)) {
|
||||
acpi_s3_reserve();
|
||||
|
||||
#ifdef CONFIG_HAVE_FSP
|
||||
/*
|
||||
* Save stack address to CMOS so that at next S3 boot,
|
||||
* we can use it as the stack address for fsp_contiue()
|
||||
*/
|
||||
fsp_save_s3_stack();
|
||||
#endif /* CONFIG_HAVE_FSP */
|
||||
#endif /* CONFIG_HAVE_ACPI_RESUME */
|
||||
if (IS_ENABLED(CONFIG_HAVE_FSP)) {
|
||||
/*
|
||||
* Save stack address to CMOS so that at next S3 boot,
|
||||
* we can use it as the stack address for fsp_contiue()
|
||||
*/
|
||||
fsp_save_s3_stack();
|
||||
}
|
||||
}
|
||||
ret = irq_first_device_type(X86_IRQT_ITSS, &itss);
|
||||
if (!ret) {
|
||||
/*
|
||||
* Snapshot the current GPIO IRQ polarities. FSP-S is about to
|
||||
* run and will set a default policy that doesn't honour boards'
|
||||
* requirements
|
||||
*/
|
||||
irq_snapshot_polarities(itss);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ int print_cpuinfo(void)
|
|||
return default_print_cpuinfo();
|
||||
}
|
||||
|
||||
void board_final_cleanup(void)
|
||||
void board_final_init(void)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -455,10 +455,15 @@ int x86_cpu_init_f(void)
|
|||
|
||||
int x86_cpu_reinit_f(void)
|
||||
{
|
||||
long addr;
|
||||
|
||||
setup_identity();
|
||||
setup_pci_ram_top();
|
||||
if (locate_coreboot_table() >= 0)
|
||||
addr = locate_coreboot_table();
|
||||
if (addr >= 0) {
|
||||
gd->arch.coreboot_table = addr;
|
||||
gd->flags |= GD_FLG_SKIP_LL_INIT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -65,14 +65,23 @@ static int snapshot_polarities(struct udevice *dev)
|
|||
int i;
|
||||
|
||||
reg_start = start / IRQS_PER_IPC;
|
||||
reg_end = (end + IRQS_PER_IPC - 1) / IRQS_PER_IPC;
|
||||
reg_end = DIV_ROUND_UP(end, IRQS_PER_IPC);
|
||||
|
||||
log_info("ITSS IRQ Polarities snapshot %p\n", priv->irq_snapshot);
|
||||
for (i = reg_start; i < reg_end; i++) {
|
||||
uint reg = PCR_ITSS_IPC0_CONF + sizeof(u32) * i;
|
||||
|
||||
priv->irq_snapshot[i] = pcr_read32(dev, reg);
|
||||
log_debug(" - %d, reg %x: irq_snapshot[i] %x\n", i, reg,
|
||||
priv->irq_snapshot[i]);
|
||||
}
|
||||
|
||||
/* Save the snapshot for use after relocation */
|
||||
gd->start_addr_sp -= sizeof(*priv);
|
||||
gd->start_addr_sp &= ~0xf;
|
||||
gd->arch.itss_priv = (void *)gd->start_addr_sp;
|
||||
memcpy(gd->arch.itss_priv, priv, sizeof(*priv));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -91,16 +100,26 @@ static void show_polarities(struct udevice *dev, const char *msg)
|
|||
static int restore_polarities(struct udevice *dev)
|
||||
{
|
||||
struct itss_priv *priv = dev_get_priv(dev);
|
||||
struct itss_priv *old_priv;
|
||||
const int start = GPIO_IRQ_START;
|
||||
const int end = GPIO_IRQ_END;
|
||||
int reg_start;
|
||||
int reg_end;
|
||||
int i;
|
||||
|
||||
/* Get the snapshot which was stored by the pre-reloc device */
|
||||
old_priv = gd->arch.itss_priv;
|
||||
if (!old_priv)
|
||||
return log_msg_ret("priv", -EFAULT);
|
||||
memcpy(priv->irq_snapshot, old_priv->irq_snapshot,
|
||||
sizeof(priv->irq_snapshot));
|
||||
|
||||
show_polarities(dev, "Before");
|
||||
log_info("priv->irq_snapshot %p\n", priv->irq_snapshot);
|
||||
|
||||
reg_start = start / IRQS_PER_IPC;
|
||||
reg_end = (end + IRQS_PER_IPC - 1) / IRQS_PER_IPC;
|
||||
reg_end = DIV_ROUND_UP(end, IRQS_PER_IPC);
|
||||
|
||||
|
||||
for (i = reg_start; i < reg_end; i++) {
|
||||
u32 mask;
|
||||
|
@ -125,6 +144,8 @@ static int restore_polarities(struct udevice *dev)
|
|||
mask &= ~((1U << irq_start) - 1);
|
||||
|
||||
reg = PCR_ITSS_IPC0_CONF + sizeof(u32) * i;
|
||||
log_debug(" - %d, reg %x: mask %x, irq_snapshot[i] %x\n",
|
||||
i, reg, mask, priv->irq_snapshot[i]);
|
||||
pcr_clrsetbits32(dev, reg, mask, mask & priv->irq_snapshot[i]);
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,9 @@
|
|||
#include <asm/pci.h>
|
||||
#include <linux/bitops.h>
|
||||
|
||||
#define PCH_P2SB_E0 0xe0
|
||||
#define HIDE_BIT BIT(0)
|
||||
|
||||
struct p2sb_platdata {
|
||||
#if CONFIG_IS_ENABLED(OF_PLATDATA)
|
||||
struct dtd_intel_p2sb dtplat;
|
||||
|
@ -127,6 +130,40 @@ static int p2sb_probe(struct udevice *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void p2sb_set_hide_bit(struct udevice *dev, bool hide)
|
||||
{
|
||||
dm_pci_clrset_config8(dev, PCH_P2SB_E0 + 1, HIDE_BIT,
|
||||
hide ? HIDE_BIT : 0);
|
||||
}
|
||||
|
||||
static int intel_p2sb_set_hide(struct udevice *dev, bool hide)
|
||||
{
|
||||
u16 vendor;
|
||||
|
||||
if (!CONFIG_IS_ENABLED(PCI))
|
||||
return -EPERM;
|
||||
p2sb_set_hide_bit(dev, hide);
|
||||
|
||||
dm_pci_read_config16(dev, PCI_VENDOR_ID, &vendor);
|
||||
if (hide && vendor != 0xffff)
|
||||
return log_msg_ret("hide", -EEXIST);
|
||||
else if (!hide && vendor != PCI_VENDOR_ID_INTEL)
|
||||
return log_msg_ret("unhide", -ENOMEDIUM);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int p2sb_remove(struct udevice *dev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = intel_p2sb_set_hide(dev, true);
|
||||
if (ret)
|
||||
return log_msg_ret("hide", ret);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int p2sb_child_post_bind(struct udevice *dev)
|
||||
{
|
||||
#if !CONFIG_IS_ENABLED(OF_PLATDATA)
|
||||
|
@ -143,6 +180,10 @@ static int p2sb_child_post_bind(struct udevice *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
struct p2sb_ops p2sb_ops = {
|
||||
.set_hide = intel_p2sb_set_hide,
|
||||
};
|
||||
|
||||
static const struct udevice_id p2sb_ids[] = {
|
||||
{ .compatible = "intel,p2sb" },
|
||||
{ }
|
||||
|
@ -153,9 +194,12 @@ U_BOOT_DRIVER(p2sb_drv) = {
|
|||
.id = UCLASS_P2SB,
|
||||
.of_match = p2sb_ids,
|
||||
.probe = p2sb_probe,
|
||||
.remove = p2sb_remove,
|
||||
.ops = &p2sb_ops,
|
||||
.ofdata_to_platdata = p2sb_ofdata_to_platdata,
|
||||
.platdata_auto_alloc_size = sizeof(struct p2sb_platdata),
|
||||
.per_child_platdata_auto_alloc_size =
|
||||
sizeof(struct p2sb_child_platdata),
|
||||
.child_post_bind = p2sb_child_post_bind,
|
||||
.flags = DM_FLAG_OS_PREPARE,
|
||||
};
|
||||
|
|
|
@ -133,8 +133,10 @@ void acpi_create_fadt(struct acpi_fadt *fadt, struct acpi_facs *facs,
|
|||
header->checksum = table_compute_checksum(fadt, header->length);
|
||||
}
|
||||
|
||||
void acpi_create_gnvs(struct acpi_global_nvs *gnvs)
|
||||
int acpi_create_gnvs(struct acpi_global_nvs *gnvs)
|
||||
{
|
||||
/* quark is a uni-processor */
|
||||
gnvs->pcnt = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -363,7 +363,7 @@ int arch_misc_init(void)
|
|||
return 0;
|
||||
}
|
||||
|
||||
void board_final_cleanup(void)
|
||||
void board_final_init(void)
|
||||
{
|
||||
struct quark_rcba *rcba;
|
||||
u32 base, val;
|
||||
|
|
|
@ -124,6 +124,7 @@ car_init_ret:
|
|||
#endif
|
||||
#else
|
||||
/*
|
||||
* Instructions for FSP1, but not FSP2:
|
||||
* U-Boot enters here twice. For the first time it comes from
|
||||
* car_init_done() with esp points to a temporary stack and esi
|
||||
* set to zero. For the second time it comes from fsp_init_done()
|
||||
|
|
|
@ -107,7 +107,7 @@ u32 acpi_fill_csrt(u32 current)
|
|||
return current;
|
||||
}
|
||||
|
||||
void acpi_create_gnvs(struct acpi_global_nvs *gnvs)
|
||||
int acpi_create_gnvs(struct acpi_global_nvs *gnvs)
|
||||
{
|
||||
struct udevice *dev;
|
||||
int ret;
|
||||
|
@ -122,4 +122,6 @@ void acpi_create_gnvs(struct acpi_global_nvs *gnvs)
|
|||
if (ret > 0)
|
||||
gnvs->pcnt = ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -117,6 +117,7 @@
|
|||
reg = <0x00000000 0 0 0 0>;
|
||||
compatible = "intel,apl-hostbridge";
|
||||
pciex-region-size = <0x10000000>;
|
||||
fspm,training-delay = <21>;
|
||||
/*
|
||||
* Parameters used by the FSP-S binary blob. This is
|
||||
* really unfortunate since these parameters mostly
|
||||
|
|
314
arch/x86/include/asm/acpi_nhlt.h
Normal file
314
arch/x86/include/asm/acpi_nhlt.h
Normal file
|
@ -0,0 +1,314 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* Copyright 2020 Google LLC
|
||||
*
|
||||
* Modified from coreboot nhlt.h
|
||||
*/
|
||||
|
||||
#ifndef _NHLT_H_
|
||||
#define _NHLT_H_
|
||||
|
||||
struct acpi_ctx;
|
||||
struct nhlt;
|
||||
struct nhlt_endpoint;
|
||||
struct nhlt_format;
|
||||
struct nhlt_format_config;
|
||||
|
||||
/*
|
||||
* Non HD Audio ACPI support. This table is typically used for Intel Smart
|
||||
* Sound Technology DSP. It provides a way to encode opaque settings in
|
||||
* the ACPI tables.
|
||||
*
|
||||
* While the structure fields of the NHLT structs are exposed below
|
||||
* the SoC/chipset code should be the only other user manipulating the
|
||||
* fields directly aside from the library itself.
|
||||
*
|
||||
* The NHLT table consists of endpoints which in turn contain different
|
||||
* supporting stream formats. Each endpoint may contain a device specific
|
||||
* configuration payload as well as each stream format.
|
||||
*
|
||||
* Most code should use the SoC variants of the functions because
|
||||
* there is required logic needed to be performed by the SoC. The SoC
|
||||
* code should be abstracting the inner details of these functions that
|
||||
* specically apply to NHLT objects for that SoC.
|
||||
*
|
||||
* An example sequence:
|
||||
*
|
||||
* nhlt = nhlt_init()
|
||||
* ep = nhlt_add_endpoint()
|
||||
* nhlt_endpoint_append_config(ep)
|
||||
* nhlt_endpoint_add_formats(ep)
|
||||
* nhlt_soc_serialise()
|
||||
*/
|
||||
|
||||
/* Obtain an nhlt object for adding endpoints. Returns NULL on error. */
|
||||
struct nhlt *nhlt_init(void);
|
||||
|
||||
/* Return the size of the NHLT table including ACPI header. */
|
||||
size_t nhlt_current_size(struct nhlt *nhlt);
|
||||
|
||||
/*
|
||||
* Helper functions for adding NHLT devices utilizing an nhlt_endp_descriptor
|
||||
* to drive the logic.
|
||||
*/
|
||||
|
||||
struct nhlt_endp_descriptor {
|
||||
/* NHLT endpoint types. */
|
||||
int link;
|
||||
int device;
|
||||
int direction;
|
||||
u16 vid;
|
||||
u16 did;
|
||||
/* Optional endpoint specific configuration data. */
|
||||
const void *cfg;
|
||||
size_t cfg_size;
|
||||
/* Formats supported for endpoint. */
|
||||
const struct nhlt_format_config *formats;
|
||||
size_t num_formats;
|
||||
};
|
||||
|
||||
/*
|
||||
* Add the number of endpoints described by each descriptor. The virtual bus
|
||||
* id for each descriptor is the default value of 0.
|
||||
* Returns < 0 on error, 0 on success.
|
||||
*/
|
||||
int nhlt_add_endpoints(struct nhlt *nhlt,
|
||||
const struct nhlt_endp_descriptor *epds,
|
||||
size_t num_epds);
|
||||
|
||||
/*
|
||||
* Add the number of endpoints associated with a single NHLT SSP instance id.
|
||||
* Each endpoint described in the endpoint descriptor array uses the provided
|
||||
* virtual bus id. Returns < 0 on error, 0 on success.
|
||||
*/
|
||||
int nhlt_add_ssp_endpoints(struct nhlt *nhlt, int virtual_bus_id,
|
||||
const struct nhlt_endp_descriptor *epds,
|
||||
size_t num_epds);
|
||||
|
||||
/*
|
||||
* Add endpoint to NHLT object. Returns NULL on error.
|
||||
*
|
||||
* generic nhlt_add_endpoint() is called by the SoC code to provide
|
||||
* the specific assumptions/uses for NHLT for that platform. All fields
|
||||
* are the NHLT enumerations found within this header file.
|
||||
*/
|
||||
struct nhlt_endpoint *nhlt_add_endpoint(struct nhlt *nhlt, int link_type,
|
||||
int device_type, int dir,
|
||||
u16 vid, u16 did);
|
||||
|
||||
/*
|
||||
* Append blob of configuration to the endpoint proper. Returns 0 on
|
||||
* success, < 0 on error. A copy of the configuration is made so any
|
||||
* resources pointed to by config can be freed after the call.
|
||||
*/
|
||||
int nhlt_endpoint_append_config(struct nhlt_endpoint *endpoint,
|
||||
const void *config, size_t config_sz);
|
||||
|
||||
/* Add a format type to the provided endpoint. Returns NULL on error. */
|
||||
struct nhlt_format *nhlt_add_format(struct nhlt_endpoint *endpoint,
|
||||
int num_channels, int sample_freq_khz,
|
||||
int container_bits_per_sample,
|
||||
int valid_bits_per_sample,
|
||||
u32 speaker_mask);
|
||||
|
||||
/*
|
||||
* Append blob of configuration to the format proper. Returns 0 on
|
||||
* success, < 0 on error. A copy of the configuration is made so any
|
||||
* resources pointed to by config can be freed after the call.
|
||||
*/
|
||||
int nhlt_format_append_config(struct nhlt_format *format, const void *config,
|
||||
size_t config_sz);
|
||||
|
||||
/*
|
||||
* Add num_formats described by formats to the endpoint. This function
|
||||
* effectively wraps nhlt_add_format() and nhlt_format_config() using the
|
||||
* data found in each nhlt_format_config object. Returns 0 on success, < 0
|
||||
* on error.
|
||||
*/
|
||||
int nhlt_endpoint_add_formats(struct nhlt_endpoint *endpoint,
|
||||
const struct nhlt_format_config *formats,
|
||||
size_t num_formats);
|
||||
|
||||
/*
|
||||
* Increment the instance id for a given link type. This function is
|
||||
* used for marking a device being completely added to the NHLT object.
|
||||
* Subsequent endpoints added to the nhlt object with the same link type
|
||||
* will use incremented instance id.
|
||||
*/
|
||||
void nhlt_next_instance(struct nhlt *nhlt, int link_type);
|
||||
|
||||
/*
|
||||
* Serialize NHLT object to ACPI table. Take in the beginning address of where
|
||||
* the table will reside and return the address of the next ACPI table. On
|
||||
* error 0 will be returned. The NHLT object is no longer valid after this
|
||||
* function is called.
|
||||
*/
|
||||
uintptr_t nhlt_serialise(struct nhlt *nhlt, uintptr_t acpi_addr);
|
||||
|
||||
/*
|
||||
* Serialize NHLT object to ACPI table. Take in the beginning address of where
|
||||
* the table will reside oem_id and oem_table_id and return the address of the
|
||||
* next ACPI table. On error 0 will be returned. The NHLT object is no longer
|
||||
* valid after this function is called.
|
||||
*/
|
||||
int nhlt_serialise_oem_overrides(struct acpi_ctx *ctx, struct nhlt *nhlt,
|
||||
const char *oem_id, const char *oem_table_id,
|
||||
u32 oem_revision);
|
||||
|
||||
int nhlt_setup(struct nhlt *nhlt, ofnode node);
|
||||
|
||||
/* Link and device types. */
|
||||
enum {
|
||||
NHLT_LINK_HDA,
|
||||
NHLT_LINK_DSP,
|
||||
NHLT_LINK_PDM,
|
||||
NHLT_LINK_SSP,
|
||||
NHLT_MAX_LINK_TYPES,
|
||||
};
|
||||
|
||||
enum {
|
||||
NHLT_SSP_DEV_BT, /* Bluetooth */
|
||||
NHLT_SSP_DEV_MODEM,
|
||||
NHLT_SSP_DEV_FM,
|
||||
NHLT_SSP_DEV_RESERVED,
|
||||
NHLT_SSP_DEV_I2S = 4,
|
||||
};
|
||||
|
||||
enum {
|
||||
NHLT_PDM_DEV,
|
||||
};
|
||||
|
||||
/* Endpoint direction. */
|
||||
enum {
|
||||
NHLT_DIR_RENDER,
|
||||
NHLT_DIR_CAPTURE,
|
||||
NHLT_DIR_BIDIRECTIONAL,
|
||||
};
|
||||
|
||||
/*
|
||||
* Channel mask for an endpoint. While they are prefixed with 'SPEAKER' the
|
||||
* channel masks are also used for capture devices
|
||||
*/
|
||||
enum {
|
||||
SPEAKER_FRONT_LEFT = BIT(0),
|
||||
SPEAKER_FRONT_RIGHT = BIT(1),
|
||||
SPEAKER_FRONT_CENTER = BIT(2),
|
||||
SPEAKER_LOW_FREQUENCY = BIT(3),
|
||||
SPEAKER_BACK_LEFT = BIT(4),
|
||||
SPEAKER_BACK_RIGHT = BIT(5),
|
||||
SPEAKER_FRONT_LEFT_OF_CENTER = BIT(6),
|
||||
SPEAKER_FRONT_RIGHT_OF_CENTER = BIT(7),
|
||||
SPEAKER_BACK_CENTER = BIT(8),
|
||||
SPEAKER_SIDE_LEFT = BIT(9),
|
||||
SPEAKER_SIDE_RIGHT = BIT(10),
|
||||
SPEAKER_TOP_CENTER = BIT(11),
|
||||
SPEAKER_TOP_FRONT_LEFT = BIT(12),
|
||||
SPEAKER_TOP_FRONT_CENTER = BIT(13),
|
||||
SPEAKER_TOP_FRONT_RIGHT = BIT(14),
|
||||
SPEAKER_TOP_BACK_LEFT = BIT(15),
|
||||
SPEAKER_TOP_BACK_CENTER = BIT(16),
|
||||
SPEAKER_TOP_BACK_RIGHT = BIT(17),
|
||||
};
|
||||
|
||||
/*
|
||||
* Supporting structures. Only SoC/chipset and the library code directly should
|
||||
* be manipulating these structures
|
||||
*/
|
||||
struct sub_format {
|
||||
u32 data1;
|
||||
u16 data2;
|
||||
u16 data3;
|
||||
u8 data4[8];
|
||||
};
|
||||
|
||||
struct nhlt_specific_config {
|
||||
u32 size;
|
||||
void *capabilities;
|
||||
};
|
||||
|
||||
struct nhlt_waveform {
|
||||
u16 tag;
|
||||
u16 num_channels;
|
||||
u32 samples_per_second;
|
||||
u32 bytes_per_second;
|
||||
u16 block_align;
|
||||
u16 bits_per_sample;
|
||||
u16 extra_size;
|
||||
u16 valid_bits_per_sample;
|
||||
u32 channel_mask;
|
||||
struct sub_format sub_format;
|
||||
};
|
||||
|
||||
struct nhlt_format {
|
||||
struct nhlt_waveform waveform;
|
||||
struct nhlt_specific_config config;
|
||||
};
|
||||
|
||||
/*
|
||||
* This struct is used by nhlt_endpoint_add_formats() for easily adding
|
||||
* waveform formats with associated settings file.
|
||||
*/
|
||||
struct nhlt_format_config {
|
||||
int num_channels;
|
||||
int sample_freq_khz;
|
||||
int container_bits_per_sample;
|
||||
int valid_bits_per_sample;
|
||||
u32 speaker_mask;
|
||||
const char *settings_file;
|
||||
};
|
||||
|
||||
/* Arbitrary max number of formats per endpoint. */
|
||||
#define MAX_FORMATS 2
|
||||
struct nhlt_endpoint {
|
||||
u32 length;
|
||||
u8 link_type;
|
||||
u8 instance_id;
|
||||
u16 vendor_id;
|
||||
u16 device_id;
|
||||
u16 revision_id;
|
||||
u32 subsystem_id;
|
||||
u8 device_type;
|
||||
u8 direction;
|
||||
u8 virtual_bus_id;
|
||||
struct nhlt_specific_config config;
|
||||
u8 num_formats;
|
||||
struct nhlt_format formats[MAX_FORMATS];
|
||||
};
|
||||
|
||||
#define MAX_ENDPOINTS 8
|
||||
struct nhlt {
|
||||
u32 subsystem_id;
|
||||
u8 num_endpoints;
|
||||
struct nhlt_endpoint endpoints[MAX_ENDPOINTS];
|
||||
u8 current_instance_id[NHLT_MAX_LINK_TYPES];
|
||||
};
|
||||
|
||||
struct nhlt_tdm_config {
|
||||
u8 virtual_slot;
|
||||
u8 config_type;
|
||||
};
|
||||
|
||||
enum {
|
||||
NHLT_TDM_BASIC,
|
||||
NHLT_TDM_MIC_ARRAY,
|
||||
};
|
||||
|
||||
struct nhlt_dmic_array_config {
|
||||
struct nhlt_tdm_config tdm_config;
|
||||
u8 array_type;
|
||||
};
|
||||
|
||||
/*
|
||||
* Microphone array definitions may be found here:
|
||||
* https://msdn.microsoft.com/en-us/library/windows/hardware/dn613960%28v=vs.85%29.aspx
|
||||
*/
|
||||
enum {
|
||||
NHLT_MIC_ARRAY_2CH_SMALL = 0xa,
|
||||
NHLT_MIC_ARRAY_2CH_BIG = 0xb,
|
||||
NHLT_MIC_ARRAY_4CH_1ST_GEOM = 0xc,
|
||||
NHLT_MIC_ARRAY_4CH_L_SHAPED = 0xd,
|
||||
NHLT_MIC_ARRAY_4CH_2ND_GEOM = 0xe,
|
||||
NHLT_MIC_ARRAY_VENDOR_DEFINED = 0xf,
|
||||
};
|
||||
|
||||
#endif
|
|
@ -35,7 +35,15 @@ int acpi_create_mcfg_mmconfig(struct acpi_mcfg_mmconfig *mmconfig, u32 base,
|
|||
u16 seg_nr, u8 start, u8 end);
|
||||
u32 acpi_fill_mcfg(u32 current);
|
||||
u32 acpi_fill_csrt(u32 current);
|
||||
void acpi_create_gnvs(struct acpi_global_nvs *gnvs);
|
||||
|
||||
/**
|
||||
* acpi_create_gnvs() - Create a GNVS (Global Non Volatile Storage) table
|
||||
*
|
||||
* @gnvs: Table to fill in
|
||||
* @return 0 if OK, -ve on error
|
||||
*/
|
||||
int acpi_create_gnvs(struct acpi_global_nvs *gnvs);
|
||||
|
||||
ulong write_acpi_tables(ulong start);
|
||||
|
||||
/**
|
||||
|
|
|
@ -57,7 +57,8 @@ int arch_fsps_preinit(void);
|
|||
*
|
||||
* @dev: Hostbridge device containing config
|
||||
* @upd: Config data to fill in
|
||||
* @return 0 if OK, -ve on error
|
||||
* @return 0 if OK, -ENOENT if OK but no MRC-cache data was found, other -ve on
|
||||
* error
|
||||
*/
|
||||
int fspm_update_config(struct udevice *dev, struct fspm_upd *upd);
|
||||
|
||||
|
|
|
@ -116,14 +116,14 @@ struct arch_global_data {
|
|||
u32 high_table_ptr;
|
||||
u32 high_table_limit;
|
||||
#endif
|
||||
#ifdef CONFIG_HAVE_ACPI_RESUME
|
||||
int prev_sleep_state; /* Previous sleep state ACPI_S0/1../5 */
|
||||
ulong backup_mem; /* Backup memory address for S3 */
|
||||
#endif
|
||||
#ifdef CONFIG_FSP_VERSION2
|
||||
struct fsp_header *fsp_s_hdr; /* Pointer to FSP-S header */
|
||||
#endif
|
||||
void *itss_priv; /* Private ITSS data pointer */
|
||||
ulong acpi_start; /* Start address of ACPI tables */
|
||||
ulong coreboot_table; /* Address of coreboot table */
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -99,7 +99,6 @@ struct pad_group {
|
|||
* groups exist inside a community
|
||||
*
|
||||
* @name: Community name
|
||||
* @acpi_path: ACPI path
|
||||
* @num_gpi_regs: number of gpi registers in community
|
||||
* @max_pads_per_group: number of pads in each group; number of pads bit-mapped
|
||||
* in each GPI status/en and Host Own Reg
|
||||
|
@ -120,7 +119,6 @@ struct pad_group {
|
|||
*/
|
||||
struct pad_community {
|
||||
const char *name;
|
||||
const char *acpi_path;
|
||||
size_t num_gpi_regs;
|
||||
size_t max_pads_per_group;
|
||||
uint first_pad;
|
||||
|
@ -263,11 +261,23 @@ int pinctrl_read_pads(struct udevice *dev, ofnode node, const char *prop,
|
|||
int pinctrl_count_pads(struct udevice *dev, u32 *pads, int size);
|
||||
|
||||
/**
|
||||
* intel_pinctrl_get_config_reg_addr() - Get address of the pin config registers
|
||||
* intel_pinctrl_get_config_reg_offset() - Get offset of pin config registers
|
||||
*
|
||||
* This works out the register offset of a pin within the p2sb region.
|
||||
*
|
||||
* @dev: Pinctrl device
|
||||
* @offset: GPIO offset within this device
|
||||
* @return register offset within the GPIO p2sb region
|
||||
* @return register offset of first register within the GPIO p2sb region
|
||||
*/
|
||||
u32 intel_pinctrl_get_config_reg_offset(struct udevice *dev, uint offset);
|
||||
|
||||
/**
|
||||
* intel_pinctrl_get_config_reg_addr() - Get address of pin config registers
|
||||
*
|
||||
* This works out the absolute address of the registers for a pin
|
||||
* @dev: Pinctrl device
|
||||
* @offset: GPIO offset within this device
|
||||
* @return register address of first register within the GPIO p2sb region
|
||||
*/
|
||||
u32 intel_pinctrl_get_config_reg_addr(struct udevice *dev, uint offset);
|
||||
|
||||
|
@ -288,6 +298,7 @@ u32 intel_pinctrl_get_config_reg(struct udevice *dev, uint offset);
|
|||
* @pad: Pad to check
|
||||
* @devp: Returns pinctrl device containing that pad
|
||||
* @offsetp: Returns offset of pad within that pinctrl device
|
||||
* @return 0 if OK, -ENOTBLK if pad number is invalid
|
||||
*/
|
||||
int intel_pinctrl_get_pad(uint pad, struct udevice **devp, uint *offsetp);
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
#define ITSS_MAX_IRQ 119
|
||||
#define IRQS_PER_IPC 32
|
||||
#define NUM_IPC_REGS ((ITSS_MAX_IRQ + IRQS_PER_IPC - 1) / IRQS_PER_IPC)
|
||||
#define NUM_IPC_REGS DIV_ROUND_UP(ITSS_MAX_IRQ, IRQS_PER_IPC)
|
||||
|
||||
/* Max PXRC registers in ITSS */
|
||||
#define MAX_PXRC_CONFIG (PCR_ITSS_PIRQH_ROUT - PCR_ITSS_PIRQA_ROUT + 1)
|
||||
|
|
|
@ -83,8 +83,6 @@ int default_print_cpuinfo(void);
|
|||
/* Set up a UART which can be used with printch(), printhex8(), etc. */
|
||||
int setup_internal_uart(int enable);
|
||||
|
||||
void setup_pcat_compatibility(void);
|
||||
|
||||
void isa_unmap_rom(u32 addr);
|
||||
u32 isa_map_rom(u32 bus_addr, int size);
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@ obj-y += init_helpers.o
|
|||
obj-y += interrupts.o
|
||||
obj-y += lpc-uclass.o
|
||||
obj-y += mpspec.o
|
||||
obj-$(CONFIG_$(SPL_TPL_)ACPIGEN) += acpi_nhlt.o
|
||||
obj-y += northbridge-uclass.o
|
||||
obj-$(CONFIG_I8259_PIC) += i8259.o
|
||||
obj-$(CONFIG_I8254_TIMER) += i8254.o
|
||||
|
|
482
arch/x86/lib/acpi_nhlt.c
Normal file
482
arch/x86/lib/acpi_nhlt.c
Normal file
|
@ -0,0 +1,482 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright 2020 Google LLC
|
||||
*
|
||||
* Modified from coreboot nhlt.c
|
||||
*/
|
||||
|
||||
#define LOG_CATEGORY LOGC_ACPI
|
||||
|
||||
#include <common.h>
|
||||
#include <binman.h>
|
||||
#include <dm.h>
|
||||
#include <log.h>
|
||||
#include <malloc.h>
|
||||
#include <tables_csum.h>
|
||||
#include <acpi/acpi_table.h>
|
||||
#include <asm/acpi_nhlt.h>
|
||||
#include <asm/unaligned.h>
|
||||
#include <dm/acpi.h>
|
||||
|
||||
#define NHLT_RID 1
|
||||
#define NHLT_SSID 1
|
||||
#define WAVEFORMAT_TAG 0xfffe
|
||||
#define DEFAULT_VIRTUAL_BUS_ID 0
|
||||
|
||||
static const struct sub_format pcm_subformat = {
|
||||
.data1 = 0x00000001,
|
||||
.data2 = 0x0000,
|
||||
.data3 = 0x0010,
|
||||
.data4 = { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 },
|
||||
};
|
||||
|
||||
struct nhlt *nhlt_init(void)
|
||||
{
|
||||
struct nhlt *nhlt;
|
||||
|
||||
nhlt = malloc(sizeof(*nhlt));
|
||||
|
||||
if (!nhlt)
|
||||
return NULL;
|
||||
|
||||
memset(nhlt, 0, sizeof(*nhlt));
|
||||
nhlt->subsystem_id = NHLT_SSID;
|
||||
|
||||
return nhlt;
|
||||
}
|
||||
|
||||
struct nhlt_endpoint *nhlt_add_endpoint(struct nhlt *nhlt, int link_type,
|
||||
int device_type, int dir,
|
||||
u16 vid, u16 did)
|
||||
{
|
||||
struct nhlt_endpoint *endp;
|
||||
|
||||
if (link_type < NHLT_LINK_HDA || link_type >= NHLT_MAX_LINK_TYPES)
|
||||
return NULL;
|
||||
|
||||
if (nhlt->num_endpoints >= MAX_ENDPOINTS)
|
||||
return NULL;
|
||||
|
||||
endp = &nhlt->endpoints[nhlt->num_endpoints];
|
||||
|
||||
endp->link_type = link_type;
|
||||
endp->instance_id = nhlt->current_instance_id[link_type];
|
||||
endp->vendor_id = vid;
|
||||
endp->device_id = did;
|
||||
endp->revision_id = NHLT_RID;
|
||||
endp->subsystem_id = nhlt->subsystem_id;
|
||||
endp->device_type = device_type;
|
||||
endp->direction = dir;
|
||||
endp->virtual_bus_id = DEFAULT_VIRTUAL_BUS_ID;
|
||||
|
||||
nhlt->num_endpoints++;
|
||||
|
||||
return endp;
|
||||
}
|
||||
|
||||
static int append_specific_config(struct nhlt_specific_config *spec_cfg,
|
||||
const void *config, size_t config_sz)
|
||||
{
|
||||
size_t new_sz;
|
||||
void *new_cfg;
|
||||
|
||||
new_sz = spec_cfg->size + config_sz;
|
||||
new_cfg = malloc(new_sz);
|
||||
if (!new_cfg)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Append new config */
|
||||
memcpy(new_cfg, spec_cfg->capabilities, spec_cfg->size);
|
||||
memcpy(new_cfg + spec_cfg->size, config, config_sz);
|
||||
|
||||
free(spec_cfg->capabilities);
|
||||
|
||||
/* Update with new config data */
|
||||
spec_cfg->size = new_sz;
|
||||
spec_cfg->capabilities = new_cfg;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nhlt_endpoint_append_config(struct nhlt_endpoint *endp, const void *config,
|
||||
size_t config_sz)
|
||||
{
|
||||
return append_specific_config(&endp->config, config, config_sz);
|
||||
}
|
||||
|
||||
struct nhlt_format *nhlt_add_format(struct nhlt_endpoint *endp,
|
||||
int num_channels, int sample_freq_khz,
|
||||
int container_bits_per_sample,
|
||||
int valid_bits_per_sample,
|
||||
uint32_t speaker_mask)
|
||||
{
|
||||
struct nhlt_format *fmt;
|
||||
struct nhlt_waveform *wave;
|
||||
|
||||
if (endp->num_formats >= MAX_FORMATS)
|
||||
return NULL;
|
||||
|
||||
fmt = &endp->formats[endp->num_formats];
|
||||
wave = &fmt->waveform;
|
||||
|
||||
wave->tag = WAVEFORMAT_TAG;
|
||||
wave->num_channels = num_channels;
|
||||
wave->samples_per_second = sample_freq_khz * 1000;
|
||||
wave->bits_per_sample = container_bits_per_sample;
|
||||
wave->extra_size = sizeof(wave->valid_bits_per_sample);
|
||||
wave->extra_size += sizeof(wave->channel_mask);
|
||||
wave->extra_size += sizeof(wave->sub_format);
|
||||
wave->valid_bits_per_sample = valid_bits_per_sample;
|
||||
wave->channel_mask = speaker_mask;
|
||||
memcpy(&wave->sub_format, &pcm_subformat, sizeof(wave->sub_format));
|
||||
|
||||
/* Calculate the dervied fields */
|
||||
wave->block_align = wave->num_channels * wave->bits_per_sample / 8;
|
||||
wave->bytes_per_second = wave->block_align * wave->samples_per_second;
|
||||
|
||||
endp->num_formats++;
|
||||
|
||||
return fmt;
|
||||
}
|
||||
|
||||
int nhlt_format_append_config(struct nhlt_format *fmt, const void *config,
|
||||
size_t config_sz)
|
||||
{
|
||||
return append_specific_config(&fmt->config, config, config_sz);
|
||||
}
|
||||
|
||||
int nhlt_endpoint_add_formats(struct nhlt_endpoint *endp,
|
||||
const struct nhlt_format_config *formats,
|
||||
size_t num_formats)
|
||||
{
|
||||
ofnode node;
|
||||
size_t i;
|
||||
|
||||
node = binman_section_find_node("private-files");
|
||||
|
||||
for (i = 0; i < num_formats; i++) {
|
||||
const struct nhlt_format_config *cfg = &formats[i];
|
||||
struct nhlt_format *fmt;
|
||||
void *data;
|
||||
int size;
|
||||
int ret;
|
||||
|
||||
fmt = nhlt_add_format(endp, cfg->num_channels,
|
||||
cfg->sample_freq_khz,
|
||||
cfg->container_bits_per_sample,
|
||||
cfg->valid_bits_per_sample,
|
||||
cfg->speaker_mask);
|
||||
if (!fmt)
|
||||
return -ENOSPC;
|
||||
|
||||
if (!cfg->settings_file)
|
||||
continue;
|
||||
|
||||
ret = binman_entry_map(node, cfg->settings_file, &data, &size);
|
||||
if (ret) {
|
||||
log_warning("Failed to find settings file %s\n",
|
||||
cfg->settings_file);
|
||||
return log_msg_ret("settings", ret);
|
||||
}
|
||||
|
||||
ret = nhlt_format_append_config(fmt, data, size);
|
||||
if (ret)
|
||||
return log_msg_ret("append", ret);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void nhlt_next_instance(struct nhlt *nhlt, int link_type)
|
||||
{
|
||||
if (link_type < NHLT_LINK_HDA || link_type >= NHLT_MAX_LINK_TYPES)
|
||||
return;
|
||||
|
||||
nhlt->current_instance_id[link_type]++;
|
||||
}
|
||||
|
||||
static size_t calc_specific_config_size(struct nhlt_specific_config *cfg)
|
||||
{
|
||||
return sizeof(cfg->size) + cfg->size;
|
||||
}
|
||||
|
||||
static size_t calc_format_size(struct nhlt_format *fmt)
|
||||
{
|
||||
size_t sz = 0;
|
||||
|
||||
/* Wave format first */
|
||||
sz += sizeof(fmt->waveform.tag);
|
||||
sz += sizeof(fmt->waveform.num_channels);
|
||||
sz += sizeof(fmt->waveform.samples_per_second);
|
||||
sz += sizeof(fmt->waveform.bytes_per_second);
|
||||
sz += sizeof(fmt->waveform.block_align);
|
||||
sz += sizeof(fmt->waveform.bits_per_sample);
|
||||
sz += sizeof(fmt->waveform.extra_size);
|
||||
sz += sizeof(fmt->waveform.valid_bits_per_sample);
|
||||
sz += sizeof(fmt->waveform.channel_mask);
|
||||
sz += sizeof(fmt->waveform.sub_format);
|
||||
|
||||
sz += calc_specific_config_size(&fmt->config);
|
||||
|
||||
return sz;
|
||||
}
|
||||
|
||||
static size_t calc_endpoint_size(struct nhlt_endpoint *endp)
|
||||
{
|
||||
int i;
|
||||
size_t sz = 0;
|
||||
|
||||
sz += sizeof(endp->length) + sizeof(endp->link_type);
|
||||
sz += sizeof(endp->instance_id) + sizeof(endp->vendor_id);
|
||||
sz += sizeof(endp->device_id) + sizeof(endp->revision_id);
|
||||
sz += sizeof(endp->subsystem_id) + sizeof(endp->device_type);
|
||||
sz += sizeof(endp->direction) + sizeof(endp->virtual_bus_id);
|
||||
sz += calc_specific_config_size(&endp->config);
|
||||
sz += sizeof(endp->num_formats);
|
||||
|
||||
for (i = 0; i < endp->num_formats; i++)
|
||||
sz += calc_format_size(&endp->formats[i]);
|
||||
|
||||
/* Adjust endpoint length to reflect current configuration */
|
||||
endp->length = sz;
|
||||
|
||||
return sz;
|
||||
}
|
||||
|
||||
static size_t calc_endpoints_size(struct nhlt *nhlt)
|
||||
{
|
||||
size_t sz = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < nhlt->num_endpoints; i++)
|
||||
sz += calc_endpoint_size(&nhlt->endpoints[i]);
|
||||
|
||||
return sz;
|
||||
}
|
||||
|
||||
static size_t calc_size(struct nhlt *nhlt)
|
||||
{
|
||||
return sizeof(nhlt->num_endpoints) + calc_endpoints_size(nhlt);
|
||||
}
|
||||
|
||||
size_t nhlt_current_size(struct nhlt *nhlt)
|
||||
{
|
||||
return calc_size(nhlt) + sizeof(struct acpi_table_header);
|
||||
}
|
||||
|
||||
static void nhlt_free_resources(struct nhlt *nhlt)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
/* Free all specific configs */
|
||||
for (i = 0; i < nhlt->num_endpoints; i++) {
|
||||
struct nhlt_endpoint *endp = &nhlt->endpoints[i];
|
||||
|
||||
free(endp->config.capabilities);
|
||||
for (j = 0; j < endp->num_formats; j++) {
|
||||
struct nhlt_format *fmt = &endp->formats[j];
|
||||
|
||||
free(fmt->config.capabilities);
|
||||
}
|
||||
}
|
||||
|
||||
/* Free nhlt object proper */
|
||||
free(nhlt);
|
||||
}
|
||||
|
||||
struct cursor {
|
||||
u8 *buf;
|
||||
};
|
||||
|
||||
static void ser8(struct cursor *cur, uint val)
|
||||
{
|
||||
*cur->buf = val;
|
||||
cur->buf += sizeof(val);
|
||||
}
|
||||
|
||||
static void ser16(struct cursor *cur, uint val)
|
||||
{
|
||||
put_unaligned_le16(val, cur->buf);
|
||||
cur->buf += sizeof(val);
|
||||
}
|
||||
|
||||
static void ser32(struct cursor *cur, uint val)
|
||||
{
|
||||
put_unaligned_le32(val, cur->buf);
|
||||
cur->buf += sizeof(val);
|
||||
}
|
||||
|
||||
static void serblob(struct cursor *cur, void *from, size_t sz)
|
||||
{
|
||||
memcpy(cur->buf, from, sz);
|
||||
cur->buf += sz;
|
||||
}
|
||||
|
||||
static void serialise_specific_config(struct nhlt_specific_config *cfg,
|
||||
struct cursor *cur)
|
||||
{
|
||||
ser32(cur, cfg->size);
|
||||
serblob(cur, cfg->capabilities, cfg->size);
|
||||
}
|
||||
|
||||
static void serialise_waveform(struct nhlt_waveform *wave, struct cursor *cur)
|
||||
{
|
||||
ser16(cur, wave->tag);
|
||||
ser16(cur, wave->num_channels);
|
||||
ser32(cur, wave->samples_per_second);
|
||||
ser32(cur, wave->bytes_per_second);
|
||||
ser16(cur, wave->block_align);
|
||||
ser16(cur, wave->bits_per_sample);
|
||||
ser16(cur, wave->extra_size);
|
||||
ser16(cur, wave->valid_bits_per_sample);
|
||||
ser32(cur, wave->channel_mask);
|
||||
ser32(cur, wave->sub_format.data1);
|
||||
ser16(cur, wave->sub_format.data2);
|
||||
ser16(cur, wave->sub_format.data3);
|
||||
serblob(cur, wave->sub_format.data4, sizeof(wave->sub_format.data4));
|
||||
}
|
||||
|
||||
static void serialise_format(struct nhlt_format *fmt, struct cursor *cur)
|
||||
{
|
||||
serialise_waveform(&fmt->waveform, cur);
|
||||
serialise_specific_config(&fmt->config, cur);
|
||||
}
|
||||
|
||||
static void serialise_endpoint(struct nhlt_endpoint *endp, struct cursor *cur)
|
||||
{
|
||||
int i;
|
||||
|
||||
ser32(cur, endp->length);
|
||||
ser8(cur, endp->link_type);
|
||||
ser8(cur, endp->instance_id);
|
||||
ser16(cur, endp->vendor_id);
|
||||
ser16(cur, endp->device_id);
|
||||
ser16(cur, endp->revision_id);
|
||||
ser32(cur, endp->subsystem_id);
|
||||
ser8(cur, endp->device_type);
|
||||
ser8(cur, endp->direction);
|
||||
ser8(cur, endp->virtual_bus_id);
|
||||
serialise_specific_config(&endp->config, cur);
|
||||
ser8(cur, endp->num_formats);
|
||||
|
||||
for (i = 0; i < endp->num_formats; i++)
|
||||
serialise_format(&endp->formats[i], cur);
|
||||
}
|
||||
|
||||
static void nhlt_serialise_endpoints(struct nhlt *nhlt, struct cursor *cur)
|
||||
{
|
||||
int i;
|
||||
|
||||
ser8(cur, nhlt->num_endpoints);
|
||||
|
||||
for (i = 0; i < nhlt->num_endpoints; i++)
|
||||
serialise_endpoint(&nhlt->endpoints[i], cur);
|
||||
}
|
||||
|
||||
int nhlt_serialise_oem_overrides(struct acpi_ctx *ctx, struct nhlt *nhlt,
|
||||
const char *oem_id, const char *oem_table_id,
|
||||
uint32_t oem_revision)
|
||||
{
|
||||
struct cursor cur;
|
||||
struct acpi_table_header *header;
|
||||
size_t sz;
|
||||
size_t oem_id_len;
|
||||
size_t oem_table_id_len;
|
||||
int ret;
|
||||
|
||||
log_info("ACPI: * NHLT\n");
|
||||
sz = nhlt_current_size(nhlt);
|
||||
|
||||
/* Create header */
|
||||
header = (void *)ctx->current;
|
||||
memset(header, '\0', sizeof(struct acpi_table_header));
|
||||
acpi_fill_header(header, "NHLT");
|
||||
header->length = sz;
|
||||
header->revision = acpi_get_table_revision(ACPITAB_NHLT);
|
||||
|
||||
if (oem_id) {
|
||||
oem_id_len = min((int)strlen(oem_id), 6);
|
||||
memcpy(header->oem_id, oem_id, oem_id_len);
|
||||
}
|
||||
if (oem_table_id) {
|
||||
oem_table_id_len = min((int)strlen(oem_table_id), 8);
|
||||
memcpy(header->oem_table_id, oem_table_id, oem_table_id_len);
|
||||
}
|
||||
header->oem_revision = oem_revision;
|
||||
|
||||
cur.buf = (void *)(header + 1);
|
||||
nhlt_serialise_endpoints(nhlt, &cur);
|
||||
|
||||
header->checksum = table_compute_checksum(header, sz);
|
||||
nhlt_free_resources(nhlt);
|
||||
|
||||
ret = acpi_add_table(ctx, ctx->current);
|
||||
if (ret)
|
||||
return log_msg_ret("add", ret);
|
||||
acpi_inc_align(ctx, sz);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _nhlt_add_single_endpoint(struct nhlt *nhlt, int virtual_bus_id,
|
||||
const struct nhlt_endp_descriptor *epd)
|
||||
{
|
||||
struct nhlt_endpoint *endp;
|
||||
int ret;
|
||||
|
||||
endp = nhlt_add_endpoint(nhlt, epd->link, epd->device, epd->direction,
|
||||
epd->vid, epd->did);
|
||||
if (!endp)
|
||||
return -EINVAL;
|
||||
|
||||
endp->virtual_bus_id = virtual_bus_id;
|
||||
|
||||
ret = nhlt_endpoint_append_config(endp, epd->cfg, epd->cfg_size);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = nhlt_endpoint_add_formats(endp, epd->formats, epd->num_formats);
|
||||
if (ret)
|
||||
return log_msg_ret("formats", ret);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _nhlt_add_endpoints(struct nhlt *nhlt, int virtual_bus_id,
|
||||
const struct nhlt_endp_descriptor *epds,
|
||||
size_t num_epds)
|
||||
{
|
||||
size_t i;
|
||||
int ret;
|
||||
|
||||
for (i = 0; i < num_epds; i++) {
|
||||
ret = _nhlt_add_single_endpoint(nhlt, virtual_bus_id, &epds[i]);
|
||||
if (ret)
|
||||
return log_ret(ret);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nhlt_add_endpoints(struct nhlt *nhlt,
|
||||
const struct nhlt_endp_descriptor *epds, size_t num_epds)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = _nhlt_add_endpoints(nhlt, DEFAULT_VIRTUAL_BUS_ID, epds, num_epds);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int nhlt_add_ssp_endpoints(struct nhlt *nhlt, int virtual_bus_id,
|
||||
const struct nhlt_endp_descriptor *epds,
|
||||
size_t num_epds)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = _nhlt_add_endpoints(nhlt, virtual_bus_id, epds, num_epds);
|
||||
if (!ret)
|
||||
nhlt_next_instance(nhlt, NHLT_LINK_SSP);
|
||||
|
||||
return ret;
|
||||
}
|
|
@ -14,6 +14,7 @@
|
|||
#include <mapmem.h>
|
||||
#include <serial.h>
|
||||
#include <version.h>
|
||||
#include <acpi/acpigen.h>
|
||||
#include <acpi/acpi_table.h>
|
||||
#include <asm/acpi/global_nvs.h>
|
||||
#include <asm/ioapic.h>
|
||||
|
@ -22,6 +23,7 @@
|
|||
#include <asm/tables.h>
|
||||
#include <asm/arch/global_nvs.h>
|
||||
#include <dm/acpi.h>
|
||||
#include <linux/err.h>
|
||||
|
||||
/*
|
||||
* IASL compiles the dsdt entries and writes the hex values
|
||||
|
@ -153,7 +155,7 @@ static void acpi_create_madt(struct acpi_madt *madt)
|
|||
/* Fill out header fields */
|
||||
acpi_fill_header(header, "APIC");
|
||||
header->length = sizeof(struct acpi_madt);
|
||||
header->revision = 4;
|
||||
header->revision = ACPI_MADT_REV_ACPI_3_0;
|
||||
|
||||
madt->lapic_addr = LAPIC_DEFAULT_BASE;
|
||||
madt->flags = ACPI_MADT_PCAT_COMPAT;
|
||||
|
@ -210,13 +212,14 @@ static void acpi_create_mcfg(struct acpi_mcfg *mcfg)
|
|||
|
||||
__weak u32 acpi_fill_csrt(u32 current)
|
||||
{
|
||||
return current;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void acpi_create_csrt(struct acpi_csrt *csrt)
|
||||
static int acpi_create_csrt(struct acpi_csrt *csrt)
|
||||
{
|
||||
struct acpi_table_header *header = &(csrt->header);
|
||||
u32 current = (u32)csrt + sizeof(struct acpi_csrt);
|
||||
uint ptr;
|
||||
|
||||
memset((void *)csrt, 0, sizeof(struct acpi_csrt));
|
||||
|
||||
|
@ -225,11 +228,16 @@ static void acpi_create_csrt(struct acpi_csrt *csrt)
|
|||
header->length = sizeof(struct acpi_csrt);
|
||||
header->revision = 0;
|
||||
|
||||
current = acpi_fill_csrt(current);
|
||||
ptr = acpi_fill_csrt(current);
|
||||
if (!ptr)
|
||||
return -ENOENT;
|
||||
current = ptr;
|
||||
|
||||
/* (Re)calculate length and checksum */
|
||||
header->length = current - (u32)csrt;
|
||||
header->checksum = table_compute_checksum((void *)csrt, header->length);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void acpi_create_spcr(struct acpi_spcr *spcr)
|
||||
|
@ -354,6 +362,25 @@ static void acpi_create_spcr(struct acpi_spcr *spcr)
|
|||
header->checksum = table_compute_checksum((void *)spcr, header->length);
|
||||
}
|
||||
|
||||
void acpi_create_ssdt(struct acpi_ctx *ctx, struct acpi_table_header *ssdt,
|
||||
const char *oem_table_id)
|
||||
{
|
||||
memset((void *)ssdt, '\0', sizeof(struct acpi_table_header));
|
||||
|
||||
acpi_fill_header(ssdt, "SSDT");
|
||||
ssdt->revision = acpi_get_table_revision(ACPITAB_SSDT);
|
||||
ssdt->aslc_revision = 1;
|
||||
ssdt->length = sizeof(struct acpi_table_header);
|
||||
|
||||
acpi_inc(ctx, sizeof(struct acpi_table_header));
|
||||
|
||||
acpi_fill_ssdt(ctx);
|
||||
|
||||
/* (Re)calculate length and checksum. */
|
||||
ssdt->length = ctx->current - (void *)ssdt;
|
||||
ssdt->checksum = table_compute_checksum((void *)ssdt, ssdt->length);
|
||||
}
|
||||
|
||||
/*
|
||||
* QEMU's version of write_acpi_tables is defined in drivers/misc/qfw.c
|
||||
*/
|
||||
|
@ -363,6 +390,7 @@ ulong write_acpi_tables(ulong start_addr)
|
|||
struct acpi_facs *facs;
|
||||
struct acpi_table_header *dsdt;
|
||||
struct acpi_fadt *fadt;
|
||||
struct acpi_table_header *ssdt;
|
||||
struct acpi_mcfg *mcfg;
|
||||
struct acpi_madt *madt;
|
||||
struct acpi_csrt *csrt;
|
||||
|
@ -385,11 +413,20 @@ ulong write_acpi_tables(ulong start_addr)
|
|||
|
||||
debug("ACPI: * DSDT\n");
|
||||
dsdt = ctx->current;
|
||||
|
||||
/* Put the table header first */
|
||||
memcpy(dsdt, &AmlCode, sizeof(struct acpi_table_header));
|
||||
acpi_inc(ctx, sizeof(struct acpi_table_header));
|
||||
|
||||
/* If the table is not empty, allow devices to inject things */
|
||||
if (dsdt->length >= sizeof(struct acpi_table_header))
|
||||
acpi_inject_dsdt(ctx);
|
||||
|
||||
/* Copy in the AML code itself if any (after the header) */
|
||||
memcpy(ctx->current,
|
||||
(char *)&AmlCode + sizeof(struct acpi_table_header),
|
||||
dsdt->length - sizeof(struct acpi_table_header));
|
||||
|
||||
acpi_inc_align(ctx, dsdt->length - sizeof(struct acpi_table_header));
|
||||
|
||||
/* Pack GNVS into the ACPI table area */
|
||||
|
@ -404,12 +441,23 @@ ulong write_acpi_tables(ulong start_addr)
|
|||
}
|
||||
}
|
||||
|
||||
/* Update DSDT checksum since we patched the GNVS address */
|
||||
/*
|
||||
* Recalculate the length and update the DSDT checksum since we patched
|
||||
* the GNVS address. Set the checksum to zero since it is part of the
|
||||
* region being checksummed.
|
||||
*/
|
||||
dsdt->length = ctx->current - (void *)dsdt;
|
||||
dsdt->checksum = 0;
|
||||
dsdt->checksum = table_compute_checksum((void *)dsdt, dsdt->length);
|
||||
|
||||
/* Fill in platform-specific global NVS variables */
|
||||
acpi_create_gnvs(ctx->current);
|
||||
/*
|
||||
* Fill in platform-specific global NVS variables. If this fails we
|
||||
* cannot return the error but this should only happen while debugging.
|
||||
*/
|
||||
addr = acpi_create_gnvs(ctx->current);
|
||||
if (IS_ERR_VALUE(addr))
|
||||
printf("Error: Failed to create GNVS\n");
|
||||
|
||||
acpi_inc_align(ctx, sizeof(struct acpi_global_nvs));
|
||||
|
||||
debug("ACPI: * FADT\n");
|
||||
|
@ -418,11 +466,13 @@ ulong write_acpi_tables(ulong start_addr)
|
|||
acpi_create_fadt(fadt, facs, dsdt);
|
||||
acpi_add_table(ctx, fadt);
|
||||
|
||||
debug("ACPI: * MADT\n");
|
||||
madt = ctx->current;
|
||||
acpi_create_madt(madt);
|
||||
acpi_inc_align(ctx, madt->header.length);
|
||||
acpi_add_table(ctx, madt);
|
||||
debug("ACPI: * SSDT\n");
|
||||
ssdt = (struct acpi_table_header *)ctx->current;
|
||||
acpi_create_ssdt(ctx, ssdt, OEM_TABLE_ID);
|
||||
if (ssdt->length > sizeof(struct acpi_table_header)) {
|
||||
acpi_inc_align(ctx, ssdt->length);
|
||||
acpi_add_table(ctx, ssdt);
|
||||
}
|
||||
|
||||
debug("ACPI: * MCFG\n");
|
||||
mcfg = ctx->current;
|
||||
|
@ -430,11 +480,18 @@ ulong write_acpi_tables(ulong start_addr)
|
|||
acpi_inc_align(ctx, mcfg->header.length);
|
||||
acpi_add_table(ctx, mcfg);
|
||||
|
||||
debug("ACPI: * MADT\n");
|
||||
madt = ctx->current;
|
||||
acpi_create_madt(madt);
|
||||
acpi_inc_align(ctx, madt->header.length);
|
||||
acpi_add_table(ctx, madt);
|
||||
|
||||
debug("ACPI: * CSRT\n");
|
||||
csrt = ctx->current;
|
||||
acpi_create_csrt(csrt);
|
||||
acpi_inc_align(ctx, csrt->header.length);
|
||||
acpi_add_table(ctx, csrt);
|
||||
if (!acpi_create_csrt(csrt)) {
|
||||
acpi_inc_align(ctx, csrt->header.length);
|
||||
acpi_add_table(ctx, csrt);
|
||||
}
|
||||
|
||||
debug("ACPI: * SPCR\n");
|
||||
spcr = ctx->current;
|
||||
|
|
|
@ -21,11 +21,11 @@ int high_table_reserve(void)
|
|||
gd->arch.high_table_ptr = gd->start_addr_sp;
|
||||
|
||||
/* clear the memory */
|
||||
#ifdef CONFIG_HAVE_ACPI_RESUME
|
||||
if (gd->arch.prev_sleep_state != ACPI_S3)
|
||||
#endif
|
||||
if (IS_ENABLED(CONFIG_HAVE_ACPI_RESUME) &&
|
||||
gd->arch.prev_sleep_state != ACPI_S3) {
|
||||
memset((void *)gd->arch.high_table_ptr, 0,
|
||||
CONFIG_HIGH_TABLE_SIZE);
|
||||
}
|
||||
|
||||
gd->start_addr_sp &= ~0xf;
|
||||
|
||||
|
|
|
@ -47,7 +47,7 @@ int fsp_init_phase_pci(void)
|
|||
return status ? -EPERM : 0;
|
||||
}
|
||||
|
||||
void board_final_cleanup(void)
|
||||
void board_final_init(void)
|
||||
{
|
||||
u32 status;
|
||||
|
||||
|
@ -60,7 +60,6 @@ void board_final_cleanup(void)
|
|||
debug("OK\n");
|
||||
}
|
||||
|
||||
#ifdef CONFIG_HAVE_ACPI_RESUME
|
||||
int fsp_save_s3_stack(void)
|
||||
{
|
||||
struct udevice *dev;
|
||||
|
@ -84,4 +83,3 @@ int fsp_save_s3_stack(void)
|
|||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -117,17 +117,21 @@ unsigned int install_e820_map(unsigned int max_entries,
|
|||
entries[num_entries].type = E820_RESERVED;
|
||||
num_entries++;
|
||||
|
||||
#ifdef CONFIG_HAVE_ACPI_RESUME
|
||||
/*
|
||||
* Everything between U-Boot's stack and ram top needs to be
|
||||
* reserved in order for ACPI S3 resume to work.
|
||||
*/
|
||||
entries[num_entries].addr = gd->start_addr_sp - CONFIG_STACK_SIZE;
|
||||
entries[num_entries].size = gd->ram_top - gd->start_addr_sp +
|
||||
CONFIG_STACK_SIZE;
|
||||
entries[num_entries].type = E820_RESERVED;
|
||||
num_entries++;
|
||||
#endif
|
||||
if (IS_ENABLED(CONFIG_HAVE_ACPI_RESUME)) {
|
||||
ulong stack_size;
|
||||
|
||||
stack_size = CONFIG_IS_ENABLED(HAVE_ACPI_RESUME,
|
||||
(CONFIG_STACK_SIZE), (0));
|
||||
/*
|
||||
* Everything between U-Boot's stack and ram top needs to be
|
||||
* reserved in order for ACPI S3 resume to work.
|
||||
*/
|
||||
entries[num_entries].addr = gd->start_addr_sp - stack_size;
|
||||
entries[num_entries].size = gd->ram_top - gd->start_addr_sp +
|
||||
stack_size;
|
||||
entries[num_entries].type = E820_RESERVED;
|
||||
num_entries++;
|
||||
}
|
||||
|
||||
return num_entries;
|
||||
}
|
||||
|
|
|
@ -46,10 +46,12 @@ int arch_fsp_init(void)
|
|||
void *nvs;
|
||||
int stack = CONFIG_FSP_TEMP_RAM_ADDR;
|
||||
int boot_mode = BOOT_FULL_CONFIG;
|
||||
#ifdef CONFIG_HAVE_ACPI_RESUME
|
||||
int prev_sleep_state = chipset_prev_sleep_state();
|
||||
gd->arch.prev_sleep_state = prev_sleep_state;
|
||||
#endif
|
||||
int prev_sleep_state;
|
||||
|
||||
if (IS_ENABLED(CONFIG_HAVE_ACPI_RESUME)) {
|
||||
prev_sleep_state = chipset_prev_sleep_state();
|
||||
gd->arch.prev_sleep_state = prev_sleep_state;
|
||||
}
|
||||
|
||||
if (!gd->arch.hob_list) {
|
||||
if (IS_ENABLED(CONFIG_ENABLE_MRC_CACHE))
|
||||
|
@ -57,8 +59,8 @@ int arch_fsp_init(void)
|
|||
else
|
||||
nvs = NULL;
|
||||
|
||||
#ifdef CONFIG_HAVE_ACPI_RESUME
|
||||
if (prev_sleep_state == ACPI_S3) {
|
||||
if (IS_ENABLED(CONFIG_HAVE_ACPI_RESUME) &&
|
||||
prev_sleep_state == ACPI_S3) {
|
||||
if (nvs == NULL) {
|
||||
/* If waking from S3 and no cache then */
|
||||
debug("No MRC cache found in S3 resume path\n");
|
||||
|
@ -79,7 +81,7 @@ int arch_fsp_init(void)
|
|||
stack = cmos_read32(CMOS_FSP_STACK_ADDR);
|
||||
boot_mode = BOOT_ON_S3_RESUME;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The first time we enter here, call fsp_init().
|
||||
* Note the execution does not return to this function,
|
||||
|
|
|
@ -27,11 +27,10 @@ int dram_init(void)
|
|||
return 0;
|
||||
}
|
||||
if (spl_phase() == PHASE_SPL) {
|
||||
#ifdef CONFIG_HAVE_ACPI_RESUME
|
||||
bool s3wake = gd->arch.prev_sleep_state == ACPI_S3;
|
||||
#else
|
||||
bool s3wake = false;
|
||||
#endif
|
||||
|
||||
s3wake = IS_ENABLED(CONFIG_HAVE_ACPI_RESUME) &&
|
||||
gd->arch.prev_sleep_state == ACPI_S3;
|
||||
|
||||
ret = fsp_memory_init(s3wake,
|
||||
IS_ENABLED(CONFIG_APL_BOOT_FROM_FAST_SPI_FLASH));
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include <common.h>
|
||||
#include <binman.h>
|
||||
#include <bootstage.h>
|
||||
#include <dm.h>
|
||||
#include <log.h>
|
||||
#include <asm/mrccache.h>
|
||||
#include <asm/fsp/fsp_infoheader.h>
|
||||
|
@ -63,8 +64,10 @@ int fsp_memory_init(bool s3wake, bool use_spi_flash)
|
|||
struct fsp_header *hdr;
|
||||
struct hob_header *hob;
|
||||
struct udevice *dev;
|
||||
int delay;
|
||||
int ret;
|
||||
|
||||
log_debug("Locating FSP\n");
|
||||
ret = fsp_locate_fsp(FSP_M, &entry, use_spi_flash, &dev, &hdr, NULL);
|
||||
if (ret)
|
||||
return log_msg_ret("locate FSP", ret);
|
||||
|
@ -76,21 +79,32 @@ int fsp_memory_init(bool s3wake, bool use_spi_flash)
|
|||
return log_msg_ret("Bad UPD signature", -EPERM);
|
||||
memcpy(&upd, fsp_upd, sizeof(upd));
|
||||
|
||||
delay = dev_read_u32_default(dev, "fspm,training-delay", 0);
|
||||
ret = fspm_update_config(dev, &upd);
|
||||
if (ret)
|
||||
return log_msg_ret("Could not setup config", ret);
|
||||
if (ret) {
|
||||
if (ret != -ENOENT)
|
||||
return log_msg_ret("Could not setup config", ret);
|
||||
} else {
|
||||
delay = 0;
|
||||
}
|
||||
|
||||
debug("SDRAM init...");
|
||||
if (delay)
|
||||
printf("SDRAM training (%d seconds)...", delay);
|
||||
else
|
||||
log_debug("SDRAM init...");
|
||||
bootstage_start(BOOTSTAGE_ID_ACCUM_FSP_M, "fsp-m");
|
||||
func = (fsp_memory_init_func)(hdr->img_base + hdr->fsp_mem_init);
|
||||
ret = func(&upd, &hob);
|
||||
bootstage_accum(BOOTSTAGE_ID_ACCUM_FSP_M);
|
||||
cpu_reinit_fpu();
|
||||
if (delay)
|
||||
printf("done\n");
|
||||
else
|
||||
log_debug("done\n");
|
||||
if (ret)
|
||||
return log_msg_ret("SDRAM init fail\n", ret);
|
||||
|
||||
gd->arch.hob_list = hob;
|
||||
debug("done\n");
|
||||
|
||||
ret = fspm_done(dev);
|
||||
if (ret)
|
||||
|
|
|
@ -32,6 +32,7 @@ int fsp_silicon_init(bool s3wake, bool use_spi_flash)
|
|||
&rom_offset);
|
||||
if (ret)
|
||||
return log_msg_ret("locate FSP", ret);
|
||||
binman_set_rom_offset(rom_offset);
|
||||
gd->arch.fsp_s_hdr = hdr;
|
||||
|
||||
/* Copy over the default config */
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <log.h>
|
||||
#include <malloc.h>
|
||||
#include <smbios.h>
|
||||
#include <acpi/acpi_table.h>
|
||||
|
@ -20,21 +21,32 @@
|
|||
*/
|
||||
typedef ulong (*table_write)(ulong addr);
|
||||
|
||||
static table_write table_write_funcs[] = {
|
||||
/**
|
||||
* struct table_info - Information about each table to write
|
||||
*
|
||||
* @name: Name of table (for debugging)
|
||||
* @write: Function to call to write this table
|
||||
*/
|
||||
struct table_info {
|
||||
const char *name;
|
||||
table_write write;
|
||||
};
|
||||
|
||||
static struct table_info table_list[] = {
|
||||
#ifdef CONFIG_GENERATE_PIRQ_TABLE
|
||||
write_pirq_routing_table,
|
||||
{ "pirq", write_pirq_routing_table },
|
||||
#endif
|
||||
#ifdef CONFIG_GENERATE_SFI_TABLE
|
||||
write_sfi_table,
|
||||
{ "sfi", write_sfi_table, },
|
||||
#endif
|
||||
#ifdef CONFIG_GENERATE_MP_TABLE
|
||||
write_mp_table,
|
||||
{ "mp", write_mp_table, },
|
||||
#endif
|
||||
#ifdef CONFIG_GENERATE_ACPI_TABLE
|
||||
write_acpi_tables,
|
||||
{ "acpi", write_acpi_tables, },
|
||||
#endif
|
||||
#ifdef CONFIG_GENERATE_SMBIOS_TABLE
|
||||
write_smbios_table,
|
||||
{ "smbios", write_smbios_table, },
|
||||
#endif
|
||||
};
|
||||
|
||||
|
@ -58,19 +70,22 @@ void write_tables(void)
|
|||
u32 rom_table_end;
|
||||
#ifdef CONFIG_SEABIOS
|
||||
u32 high_table, table_size;
|
||||
struct memory_area cfg_tables[ARRAY_SIZE(table_write_funcs) + 1];
|
||||
struct memory_area cfg_tables[ARRAY_SIZE(table_list) + 1];
|
||||
#endif
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(table_write_funcs); i++) {
|
||||
rom_table_end = table_write_funcs[i](rom_table_start);
|
||||
debug("Writing tables to %x:\n", rom_table_start);
|
||||
for (i = 0; i < ARRAY_SIZE(table_list); i++) {
|
||||
const struct table_info *table = &table_list[i];
|
||||
|
||||
rom_table_end = table->write(rom_table_start);
|
||||
rom_table_end = ALIGN(rom_table_end, ROM_TABLE_ALIGN);
|
||||
|
||||
#ifdef CONFIG_SEABIOS
|
||||
table_size = rom_table_end - rom_table_start;
|
||||
high_table = (u32)high_table_malloc(table_size);
|
||||
if (high_table) {
|
||||
table_write_funcs[i](high_table);
|
||||
table->write(high_table);
|
||||
|
||||
cfg_tables[i].start = high_table;
|
||||
cfg_tables[i].size = table_size;
|
||||
|
@ -79,6 +94,8 @@ void write_tables(void)
|
|||
}
|
||||
#endif
|
||||
|
||||
debug("- wrote '%s' to %x, end %x\n", table->name,
|
||||
rom_table_start, rom_table_end);
|
||||
rom_table_start = rom_table_end;
|
||||
}
|
||||
|
||||
|
@ -87,4 +104,5 @@ void write_tables(void)
|
|||
cfg_tables[i].size = 0;
|
||||
write_coreboot_table(CB_TABLE_ADDR, cfg_tables);
|
||||
#endif
|
||||
debug("- done writing tables\n");
|
||||
}
|
||||
|
|
|
@ -304,13 +304,6 @@ int setup_zimage(struct boot_params *setup_base, char *cmd_line, int auto_boot,
|
|||
return 0;
|
||||
}
|
||||
|
||||
void setup_pcat_compatibility(void)
|
||||
__attribute__((weak, alias("__setup_pcat_compatibility")));
|
||||
|
||||
void __setup_pcat_compatibility(void)
|
||||
{
|
||||
}
|
||||
|
||||
int do_zboot(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
|
||||
{
|
||||
struct boot_params *base_ptr;
|
||||
|
@ -323,9 +316,6 @@ int do_zboot(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
|
|||
|
||||
disable_interrupts();
|
||||
|
||||
/* Setup board for maximum PC/AT Compatibility */
|
||||
setup_pcat_compatibility();
|
||||
|
||||
if (argc >= 2) {
|
||||
/* argv[1] holds the address of the bzImage */
|
||||
s = argv[1];
|
||||
|
|
15
cmd/acpi.c
15
cmd/acpi.c
|
@ -153,6 +153,17 @@ static int do_acpi_list(struct cmd_tbl *cmdtp, int flag, int argc,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int do_acpi_items(struct cmd_tbl *cmdtp, int flag, int argc,
|
||||
char *const argv[])
|
||||
{
|
||||
bool dump_contents;
|
||||
|
||||
dump_contents = argc >= 2 && !strcmp("-d", argv[1]);
|
||||
acpi_dump_items(dump_contents ? ACPI_DUMP_CONTENTS : ACPI_DUMP_LIST);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_acpi_dump(struct cmd_tbl *cmdtp, int flag, int argc,
|
||||
char *const argv[])
|
||||
{
|
||||
|
@ -160,8 +171,6 @@ static int do_acpi_dump(struct cmd_tbl *cmdtp, int flag, int argc,
|
|||
char sig[ACPI_NAME_LEN];
|
||||
int ret;
|
||||
|
||||
if (argc < 2)
|
||||
return CMD_RET_USAGE;
|
||||
name = argv[1];
|
||||
if (strlen(name) != ACPI_NAME_LEN) {
|
||||
printf("Table name '%s' must be four characters\n", name);
|
||||
|
@ -179,8 +188,10 @@ static int do_acpi_dump(struct cmd_tbl *cmdtp, int flag, int argc,
|
|||
|
||||
static char acpi_help_text[] =
|
||||
"list - list ACPI tables\n"
|
||||
"acpi items [-d] - List/dump each piece of ACPI data from devices\n"
|
||||
"acpi dump <name> - Dump ACPI table";
|
||||
|
||||
U_BOOT_CMD_WITH_SUBCMDS(acpi, "ACPI tables", acpi_help_text,
|
||||
U_BOOT_SUBCMD_MKENT(list, 1, 1, do_acpi_list),
|
||||
U_BOOT_SUBCMD_MKENT(items, 2, 1, do_acpi_items),
|
||||
U_BOOT_SUBCMD_MKENT(dump, 2, 1, do_acpi_dump));
|
||||
|
|
|
@ -95,6 +95,7 @@ CONFIG_TPM2_CR50_I2C=y
|
|||
CONFIG_USB_XHCI_HCD=y
|
||||
CONFIG_USB_STORAGE=y
|
||||
CONFIG_USB_KEYBOARD=y
|
||||
CONFIG_VIDEO_COPY=y
|
||||
CONFIG_SPL_FS_CBFS=y
|
||||
# CONFIG_SPL_USE_TINY_PRINTF is not set
|
||||
CONFIG_TPL_USE_TINY_PRINTF=y
|
||||
|
|
|
@ -149,7 +149,9 @@ CONFIG_P2SB=y
|
|||
CONFIG_PWRSEQ=y
|
||||
CONFIG_SPL_PWRSEQ=y
|
||||
CONFIG_I2C_EEPROM=y
|
||||
CONFIG_MMC_PCI=y
|
||||
CONFIG_MMC_SANDBOX=y
|
||||
CONFIG_MMC_SDHCI=y
|
||||
CONFIG_MTD=y
|
||||
CONFIG_SPI_FLASH_SANDBOX=y
|
||||
CONFIG_SPI_FLASH_ATMEL=y
|
||||
|
@ -208,6 +210,8 @@ CONFIG_SANDBOX_SERIAL=y
|
|||
CONFIG_SMEM=y
|
||||
CONFIG_SANDBOX_SMEM=y
|
||||
CONFIG_SOUND=y
|
||||
CONFIG_SOUND_DA7219=y
|
||||
CONFIG_SOUND_MAX98357A=y
|
||||
CONFIG_SOUND_SANDBOX=y
|
||||
CONFIG_SANDBOX_SPI=y
|
||||
CONFIG_SPMI=y
|
||||
|
|
|
@ -134,3 +134,12 @@ Example
|
|||
phandlepart = <&mmc 1>;
|
||||
};
|
||||
};
|
||||
|
||||
u-boot,acpi-ssdt-order
|
||||
----------------------
|
||||
|
||||
This provides the ordering to use when writing device data to the ACPI SSDT
|
||||
(Secondary System Descriptor Table). Each cell is a phandle pointer to a device
|
||||
node to add. The ACPI information is written in this order.
|
||||
|
||||
If the ordering does not include all nodes, an error is generated.
|
||||
|
|
|
@ -17,6 +17,10 @@ the acpi,compatible property.
|
|||
System) Device Name)
|
||||
- acpi,hid : Contains the string to use as the HID (Hardware ID)
|
||||
identifier _HID
|
||||
- acpi,path : Specifies the full ACPI path for a device. This overrides the
|
||||
normal path built from the driver-model hierarchy
|
||||
- acpi,name : Provides the ACPI name for a device, which is a string consisting
|
||||
of four alphanumeric character (upper case)
|
||||
- acpi,uid : _UID value for device
|
||||
- linux,probed : Tells U-Boot to add 'linux,probed' to the ACPI tables so that
|
||||
Linux will only load the driver if the device can be detected (e.g. on I2C
|
||||
|
@ -34,3 +38,35 @@ elan_touchscreen: elan-touchscreen@10 {
|
|||
interrupts-extended = <&acpi_gpe GPIO_21_IRQ IRQ_TYPE_EDGE_FALLING>;
|
||||
linux,probed;
|
||||
};
|
||||
|
||||
pcie-a0@14,0 {
|
||||
reg = <0x0000a000 0 0 0 0>;
|
||||
acpi,name = "RP01";
|
||||
wifi: wifi {
|
||||
compatible = "intel,generic-wifi";
|
||||
acpi,ddn = "Intel WiFi";
|
||||
acpi,name = "WF00";
|
||||
interrupts-extended = <&acpi_gpe 0x3c 0>;
|
||||
};
|
||||
};
|
||||
|
||||
p2sb: p2sb@d,0 {
|
||||
u-boot,dm-pre-reloc;
|
||||
reg = <0x02006810 0 0 0 0>;
|
||||
compatible = "intel,apl-p2sb";
|
||||
early-regs = <IOMAP_P2SB_BAR 0x100000>;
|
||||
pci,no-autoconfig;
|
||||
|
||||
n {
|
||||
compatible = "intel,apl-pinctrl";
|
||||
u-boot,dm-pre-reloc;
|
||||
intel,p2sb-port-id = <PID_GPIO_N>;
|
||||
acpi,path = "\\_SB.GPO0";
|
||||
gpio_n: gpio-n {
|
||||
compatible = "intel,gpio";
|
||||
u-boot,dm-pre-reloc;
|
||||
gpio-controller;
|
||||
#gpio-cells = <2>;
|
||||
linux-name = "INT3452:00";
|
||||
};
|
||||
};
|
||||
|
|
|
@ -17,6 +17,10 @@ values of the FSP-M are used.
|
|||
[2] https://github.com/IntelFsp/FSP/tree/master/ApolloLakeFspBinPkg/Docs
|
||||
|
||||
Optional properties:
|
||||
- fspm,training-delay: Time taken to train DDR memory if there is no cached MRC
|
||||
data, in seconds. This is used to show a message if possible. For Chromebook
|
||||
Coral this is typically 21 seconds. For an APL board with 1GB of RAM, it may
|
||||
be only 6 seconds.
|
||||
- fspm,serial-debug-port-address: Debug Serial Port Base address
|
||||
- fspm,serial-debug-port-type: Debug Serial Port Type
|
||||
0: NONE
|
||||
|
|
113
doc/device-tree-bindings/sound/da7219.txt
Normal file
113
doc/device-tree-bindings/sound/da7219.txt
Normal file
|
@ -0,0 +1,113 @@
|
|||
Dialog Semiconductor DA7219 Audio Codec bindings
|
||||
|
||||
DA7219 is an audio codec with advanced accessory detect features.
|
||||
|
||||
======
|
||||
|
||||
Required properties:
|
||||
- compatible : Should be "dlg,da7219"
|
||||
- reg: Specifies the I2C slave address
|
||||
|
||||
- interrupts : IRQ line info for DA7219.
|
||||
(See Documentation/devicetree/bindings/interrupt-controller/interrupts.txt for
|
||||
further information relating to interrupt properties)
|
||||
|
||||
- VDD-supply: VDD power supply for the device
|
||||
- VDDMIC-supply: VDDMIC power supply for the device
|
||||
- VDDIO-supply: VDDIO power supply for the device
|
||||
(See Documentation/devicetree/bindings/regulator/regulator.txt for further
|
||||
information relating to regulators)
|
||||
|
||||
Optional properties:
|
||||
- interrupt-names : Name associated with interrupt line. Should be "wakeup" if
|
||||
interrupt is to be used to wake system, otherwise "irq" should be used.
|
||||
- wakeup-source: Flag to indicate this device can wake system (suspend/resume).
|
||||
|
||||
- #clock-cells : Should be set to '<0>', only one clock source provided;
|
||||
- clock-output-names : Name given for DAI clocks output;
|
||||
|
||||
- clocks : phandle and clock specifier for codec MCLK.
|
||||
- clock-names : Clock name string for 'clocks' attribute, should be "mclk".
|
||||
|
||||
- dlg,micbias-lvl : Voltage (mV) for Mic Bias
|
||||
[<1600>, <1800>, <2000>, <2200>, <2400>, <2600>]
|
||||
- dlg,mic-amp-in-sel : Mic input source type
|
||||
["diff", "se_p", "se_n"]
|
||||
- dlg,mclk-name : String name of MCLK for ACPI
|
||||
|
||||
Deprecated properties:
|
||||
- dlg,ldo-lvl : Required internal LDO voltage (mV) level for digital engine
|
||||
(LDO unavailable in production HW so property no longer required).
|
||||
|
||||
======
|
||||
|
||||
Child node - 'da7219_aad':
|
||||
|
||||
Optional properties:
|
||||
- dlg,micbias-pulse-lvl : Mic bias higher voltage pulse level (mV).
|
||||
[<2800>, <2900>]
|
||||
- dlg,micbias-pulse-time : Mic bias higher voltage pulse duration (ms)
|
||||
- dlg,btn-cfg : Periodic button press measurements for 4-pole jack (ms)
|
||||
[<2>, <5>, <10>, <50>, <100>, <200>, <500>]
|
||||
- dlg,mic-det-thr : Impedance threshold for mic detection measurement (Ohms)
|
||||
[<200>, <500>, <750>, <1000>]
|
||||
- dlg,jack-ins-deb : Debounce time for jack insertion (ms)
|
||||
[<5>, <10>, <20>, <50>, <100>, <200>, <500>, <1000>]
|
||||
- dlg,jack-det-rate: Jack type detection latency (3/4 pole)
|
||||
["32ms_64ms", "64ms_128ms", "128ms_256ms", "256ms_512ms"]
|
||||
- dlg,jack-rem-deb : Debounce time for jack removal (ms)
|
||||
[<1>, <5>, <10>, <20>]
|
||||
- dlg,a-d-btn-thr : Impedance threshold between buttons A and D
|
||||
[0x0 - 0xFF]
|
||||
- dlg,d-b-btn-thr : Impedance threshold between buttons D and B
|
||||
[0x0 - 0xFF]
|
||||
- dlg,b-c-btn-thr : Impedance threshold between buttons B and C
|
||||
[0x0 - 0xFF]
|
||||
- dlg,c-mic-btn-thr : Impedance threshold between button C and Mic
|
||||
[0x0 - 0xFF]
|
||||
- dlg,btn-avg : Number of 8-bit readings for averaged button measurement
|
||||
[<1>, <2>, <4>, <8>]
|
||||
- dlg,adc-1bit-rpt : Repeat count for 1-bit button measurement
|
||||
[<1>, <2>, <4>, <8>]
|
||||
|
||||
======
|
||||
|
||||
Example:
|
||||
|
||||
codec: da7219@1a {
|
||||
compatible = "dlg,da7219";
|
||||
reg = <0x1a>;
|
||||
|
||||
interrupt-parent = <&gpio6>;
|
||||
interrupts = <11 IRQ_TYPE_LEVEL_LOW>;
|
||||
|
||||
VDD-supply = <®_audio>;
|
||||
VDDMIC-supply = <®_audio>;
|
||||
VDDIO-supply = <®_audio>;
|
||||
|
||||
#clock-cells = <0>;
|
||||
clock-output-names = "dai-clks";
|
||||
|
||||
clocks = <&clks 201>;
|
||||
clock-names = "mclk";
|
||||
|
||||
dlg,ldo-lvl = <1200>;
|
||||
dlg,micbias-lvl = <2600>;
|
||||
dlg,mic-amp-in-sel = "diff";
|
||||
|
||||
da7219_aad {
|
||||
dlg,btn-cfg = <50>;
|
||||
dlg,mic-det-thr = <500>;
|
||||
dlg,jack-ins-deb = <20>;
|
||||
dlg,jack-det-rate = "32ms_64ms";
|
||||
dlg,jack-rem-deb = <1>;
|
||||
|
||||
dlg,a-d-btn-thr = <0xa>;
|
||||
dlg,d-b-btn-thr = <0x16>;
|
||||
dlg,b-c-btn-thr = <0x21>;
|
||||
dlg,c-mic-btn-thr = <0x3E>;
|
||||
|
||||
dlg,btn-avg = <4>;
|
||||
dlg,adc-1bit-rpt = <1>;
|
||||
};
|
||||
};
|
22
doc/device-tree-bindings/sound/max98357a.txt
Normal file
22
doc/device-tree-bindings/sound/max98357a.txt
Normal file
|
@ -0,0 +1,22 @@
|
|||
Maxim MAX98357A audio DAC
|
||||
|
||||
This node models the Maxim MAX98357A DAC.
|
||||
|
||||
Required properties:
|
||||
- compatible : "maxim,max98357a"
|
||||
|
||||
Optional properties:
|
||||
- sdmode-gpios : GPIO specifier for the chip's SD_MODE pin.
|
||||
If this option is not specified then driver does not manage
|
||||
the pin state (e.g. chip is always on).
|
||||
- sdmode-delay : specify delay time for SD_MODE pin.
|
||||
If this option is specified, which means it's required i2s clocks
|
||||
ready before SD_MODE is unmuted in order to avoid the speaker pop noise.
|
||||
It's observed that 5ms is sufficient.
|
||||
|
||||
Example:
|
||||
|
||||
max98357a {
|
||||
compatible = "maxim,max98357a";
|
||||
sdmode-gpios = <&qcom_pinmux 25 0>;
|
||||
};
|
|
@ -270,7 +270,7 @@ config DM_DEV_READ_INLINE
|
|||
|
||||
config ACPIGEN
|
||||
bool "Support ACPI table generation in driver model"
|
||||
default y if SANDBOX || GENERATE_ACPI_TABLE
|
||||
default y if SANDBOX || (GENERATE_ACPI_TABLE && !QEMU)
|
||||
help
|
||||
This option enables generation of ACPI tables using driver-model
|
||||
devices. It adds a new operation struct to each driver, to support
|
||||
|
|
|
@ -11,18 +11,51 @@
|
|||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <log.h>
|
||||
#include <malloc.h>
|
||||
#include <acpi/acpi_device.h>
|
||||
#include <dm/acpi.h>
|
||||
#include <dm/device-internal.h>
|
||||
#include <dm/root.h>
|
||||
|
||||
#define MAX_ACPI_ITEMS 100
|
||||
|
||||
/* Type of table that we collected */
|
||||
enum gen_type_t {
|
||||
TYPE_NONE,
|
||||
TYPE_SSDT,
|
||||
TYPE_DSDT,
|
||||
};
|
||||
|
||||
/* Type of method to call */
|
||||
enum method_t {
|
||||
METHOD_WRITE_TABLES,
|
||||
METHOD_FILL_SSDT,
|
||||
METHOD_INJECT_DSDT,
|
||||
METHOD_SETUP_NHLT,
|
||||
};
|
||||
|
||||
/* Prototype for all methods */
|
||||
typedef int (*acpi_method)(const struct udevice *dev, struct acpi_ctx *ctx);
|
||||
|
||||
/**
|
||||
* struct acpi_item - Holds info about ACPI data generated by a driver method
|
||||
*
|
||||
* @dev: Device that generated this data
|
||||
* @type: Table type it refers to
|
||||
* @buf: Buffer containing the data
|
||||
* @size: Size of the data in bytes
|
||||
*/
|
||||
struct acpi_item {
|
||||
struct udevice *dev;
|
||||
enum gen_type_t type;
|
||||
char *buf;
|
||||
int size;
|
||||
};
|
||||
|
||||
/* List of ACPI items collected */
|
||||
static struct acpi_item acpi_item[MAX_ACPI_ITEMS];
|
||||
static int item_count;
|
||||
|
||||
int acpi_copy_name(char *out_name, const char *name)
|
||||
{
|
||||
strncpy(out_name, name, ACPI_NAME_LEN);
|
||||
|
@ -34,12 +67,173 @@ int acpi_copy_name(char *out_name, const char *name)
|
|||
int acpi_get_name(const struct udevice *dev, char *out_name)
|
||||
{
|
||||
struct acpi_ops *aops;
|
||||
const char *name;
|
||||
int ret;
|
||||
|
||||
aops = device_get_acpi_ops(dev);
|
||||
if (aops && aops->get_name)
|
||||
return aops->get_name(dev, out_name);
|
||||
name = dev_read_string(dev, "acpi,name");
|
||||
if (name)
|
||||
return acpi_copy_name(out_name, name);
|
||||
ret = acpi_device_infer_name(dev, out_name);
|
||||
if (ret)
|
||||
return log_msg_ret("dev", ret);
|
||||
|
||||
return -ENOSYS;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int acpi_get_path(const struct udevice *dev, char *out_path, int maxlen)
|
||||
{
|
||||
const char *path;
|
||||
int ret;
|
||||
|
||||
path = dev_read_string(dev, "acpi,path");
|
||||
if (path) {
|
||||
if (strlen(path) >= maxlen)
|
||||
return -E2BIG;
|
||||
strcpy(out_path, path);
|
||||
return 0;
|
||||
}
|
||||
ret = acpi_device_path(dev, out_path, maxlen);
|
||||
if (ret)
|
||||
return log_msg_ret("dev", ret);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* acpi_add_item() - Add a new item to the list of data collected
|
||||
*
|
||||
* @ctx: ACPI context
|
||||
* @dev: Device that generated the data
|
||||
* @type: Table type it refers to
|
||||
* @start: The start of the data (the end is obtained from ctx->current)
|
||||
* @return 0 if OK, -ENOSPC if too many items, -ENOMEM if out of memory
|
||||
*/
|
||||
static int acpi_add_item(struct acpi_ctx *ctx, struct udevice *dev,
|
||||
enum gen_type_t type, void *start)
|
||||
{
|
||||
struct acpi_item *item;
|
||||
void *end = ctx->current;
|
||||
|
||||
if (item_count == MAX_ACPI_ITEMS) {
|
||||
log_err("Too many items\n");
|
||||
return log_msg_ret("mem", -ENOSPC);
|
||||
}
|
||||
|
||||
item = &acpi_item[item_count];
|
||||
item->dev = dev;
|
||||
item->type = type;
|
||||
item->size = end - start;
|
||||
if (!item->size)
|
||||
return 0;
|
||||
item->buf = malloc(item->size);
|
||||
if (!item->buf)
|
||||
return log_msg_ret("mem", -ENOMEM);
|
||||
memcpy(item->buf, start, item->size);
|
||||
item_count++;
|
||||
log_debug("* %s: Added type %d, %p, size %x\n", dev->name, type, start,
|
||||
item->size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void acpi_dump_items(enum acpi_dump_option option)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < item_count; i++) {
|
||||
struct acpi_item *item = &acpi_item[i];
|
||||
|
||||
printf("dev '%s', type %d, size %x\n", item->dev->name,
|
||||
item->type, item->size);
|
||||
if (option == ACPI_DUMP_CONTENTS) {
|
||||
print_buffer(0, item->buf, 1, item->size, 0);
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static struct acpi_item *find_acpi_item(const char *devname)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < item_count; i++) {
|
||||
struct acpi_item *item = &acpi_item[i];
|
||||
|
||||
if (!strcmp(devname, item->dev->name))
|
||||
return item;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* sort_acpi_item_type - Sort the ACPI items into the desired order
|
||||
*
|
||||
* This looks up the ordering in the device tree and then adds each item one by
|
||||
* one into the supplied buffer
|
||||
*
|
||||
* @ctx: ACPI context
|
||||
* @start: Start position to put the sorted items. The items will follow each
|
||||
* other in sorted order
|
||||
* @type: Type of items to sort
|
||||
* @return 0 if OK, -ve on error
|
||||
*/
|
||||
static int sort_acpi_item_type(struct acpi_ctx *ctx, void *start,
|
||||
enum gen_type_t type)
|
||||
{
|
||||
const u32 *order;
|
||||
int size;
|
||||
int count;
|
||||
void *ptr;
|
||||
void *end = ctx->current;
|
||||
|
||||
ptr = start;
|
||||
order = ofnode_read_chosen_prop(type == TYPE_DSDT ?
|
||||
"u-boot,acpi-dsdt-order" :
|
||||
"u-boot,acpi-ssdt-order", &size);
|
||||
if (!order) {
|
||||
log_warning("Failed to find ordering, leaving as is\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* This algorithm rewrites the context buffer without changing its
|
||||
* length. So there is no need to update ctx-current
|
||||
*/
|
||||
count = size / sizeof(u32);
|
||||
while (count--) {
|
||||
struct acpi_item *item;
|
||||
const char *name;
|
||||
ofnode node;
|
||||
|
||||
node = ofnode_get_by_phandle(fdt32_to_cpu(*order++));
|
||||
name = ofnode_get_name(node);
|
||||
item = find_acpi_item(name);
|
||||
if (!item) {
|
||||
log_err("Failed to find item '%s'\n", name);
|
||||
return log_msg_ret("find", -ENOENT);
|
||||
}
|
||||
if (item->type == type) {
|
||||
log_debug(" - add %s\n", item->dev->name);
|
||||
memcpy(ptr, item->buf, item->size);
|
||||
ptr += item->size;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If the sort order is missing an item then the output will be too
|
||||
* small. Report this error since the item needs to be added to the
|
||||
* ordering for the ACPI tables to be complete.
|
||||
*/
|
||||
if (ptr != end) {
|
||||
log_warning("*** Missing bytes: ptr=%p, end=%p\n", ptr, end);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
acpi_method acpi_get_method(struct udevice *dev, enum method_t method)
|
||||
|
@ -51,6 +245,12 @@ acpi_method acpi_get_method(struct udevice *dev, enum method_t method)
|
|||
switch (method) {
|
||||
case METHOD_WRITE_TABLES:
|
||||
return aops->write_tables;
|
||||
case METHOD_FILL_SSDT:
|
||||
return aops->fill_ssdt;
|
||||
case METHOD_INJECT_DSDT:
|
||||
return aops->inject_dsdt;
|
||||
case METHOD_SETUP_NHLT:
|
||||
return aops->setup_nhlt;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -58,7 +258,7 @@ acpi_method acpi_get_method(struct udevice *dev, enum method_t method)
|
|||
}
|
||||
|
||||
int acpi_recurse_method(struct acpi_ctx *ctx, struct udevice *parent,
|
||||
enum method_t method)
|
||||
enum method_t method, enum gen_type_t type)
|
||||
{
|
||||
struct udevice *dev;
|
||||
acpi_method func;
|
||||
|
@ -66,6 +266,8 @@ int acpi_recurse_method(struct acpi_ctx *ctx, struct udevice *parent,
|
|||
|
||||
func = acpi_get_method(parent, method);
|
||||
if (func) {
|
||||
void *start = ctx->current;
|
||||
|
||||
log_debug("\n");
|
||||
log_debug("- %s %p\n", parent->name, func);
|
||||
ret = device_ofdata_to_platdata(parent);
|
||||
|
@ -74,9 +276,16 @@ int acpi_recurse_method(struct acpi_ctx *ctx, struct udevice *parent,
|
|||
ret = func(parent, ctx);
|
||||
if (ret)
|
||||
return log_msg_ret("func", ret);
|
||||
|
||||
/* Add the item to the internal list */
|
||||
if (type != TYPE_NONE) {
|
||||
ret = acpi_add_item(ctx, parent, type, start);
|
||||
if (ret)
|
||||
return log_msg_ret("add", ret);
|
||||
}
|
||||
}
|
||||
device_foreach_child(dev, parent) {
|
||||
ret = acpi_recurse_method(ctx, dev, method);
|
||||
ret = acpi_recurse_method(ctx, dev, method, type);
|
||||
if (ret)
|
||||
return log_msg_ret("recurse", ret);
|
||||
}
|
||||
|
@ -84,13 +293,59 @@ int acpi_recurse_method(struct acpi_ctx *ctx, struct udevice *parent,
|
|||
return 0;
|
||||
}
|
||||
|
||||
int acpi_fill_ssdt(struct acpi_ctx *ctx)
|
||||
{
|
||||
void *start = ctx->current;
|
||||
int ret;
|
||||
|
||||
log_debug("Writing SSDT tables\n");
|
||||
item_count = 0;
|
||||
ret = acpi_recurse_method(ctx, dm_root(), METHOD_FILL_SSDT, TYPE_SSDT);
|
||||
log_debug("Writing SSDT finished, err=%d\n", ret);
|
||||
ret = sort_acpi_item_type(ctx, start, TYPE_SSDT);
|
||||
if (ret)
|
||||
return log_msg_ret("build", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int acpi_inject_dsdt(struct acpi_ctx *ctx)
|
||||
{
|
||||
void *start = ctx->current;
|
||||
int ret;
|
||||
|
||||
log_debug("Writing DSDT tables\n");
|
||||
item_count = 0;
|
||||
ret = acpi_recurse_method(ctx, dm_root(), METHOD_INJECT_DSDT,
|
||||
TYPE_DSDT);
|
||||
log_debug("Writing DSDT finished, err=%d\n", ret);
|
||||
ret = sort_acpi_item_type(ctx, start, TYPE_DSDT);
|
||||
if (ret)
|
||||
return log_msg_ret("build", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int acpi_write_dev_tables(struct acpi_ctx *ctx)
|
||||
{
|
||||
int ret;
|
||||
|
||||
log_debug("Writing device tables\n");
|
||||
ret = acpi_recurse_method(ctx, dm_root(), METHOD_WRITE_TABLES);
|
||||
ret = acpi_recurse_method(ctx, dm_root(), METHOD_WRITE_TABLES,
|
||||
TYPE_NONE);
|
||||
log_debug("Writing finished, err=%d\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int acpi_setup_nhlt(struct acpi_ctx *ctx, struct nhlt *nhlt)
|
||||
{
|
||||
int ret;
|
||||
|
||||
log_debug("Setup NHLT\n");
|
||||
ctx->nhlt = nhlt;
|
||||
ret = acpi_recurse_method(ctx, dm_root(), METHOD_SETUP_NHLT, TYPE_NONE);
|
||||
log_debug("Setup finished, err=%d\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include <log.h>
|
||||
#include <malloc.h>
|
||||
#include <linux/libfdt.h>
|
||||
#include <dm/acpi.h>
|
||||
#include <dm/device.h>
|
||||
#include <dm/device-internal.h>
|
||||
#include <dm/lists.h>
|
||||
|
@ -377,10 +378,22 @@ int dm_init_and_scan(bool pre_reloc_only)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ACPIGEN
|
||||
static int root_acpi_get_name(const struct udevice *dev, char *out_name)
|
||||
{
|
||||
return acpi_copy_name(out_name, "\\_SB");
|
||||
}
|
||||
|
||||
struct acpi_ops root_acpi_ops = {
|
||||
.get_name = root_acpi_get_name,
|
||||
};
|
||||
#endif
|
||||
|
||||
/* This is the root driver - all drivers are children of this */
|
||||
U_BOOT_DRIVER(root_driver) = {
|
||||
.name = "root_driver",
|
||||
.id = UCLASS_ROOT,
|
||||
ACPI_OPS_PTR(&root_acpi_ops)
|
||||
};
|
||||
|
||||
/* This is the root uclass */
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include <errno.h>
|
||||
#include <fdtdec.h>
|
||||
#include <malloc.h>
|
||||
#include <acpi/acpi_device.h>
|
||||
#include <asm/gpio.h>
|
||||
#include <dm/device_compat.h>
|
||||
#include <linux/bug.h>
|
||||
|
@ -855,6 +856,27 @@ int gpio_get_status(struct udevice *dev, int offset, char *buf, int buffsize)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#if CONFIG_IS_ENABLED(ACPIGEN)
|
||||
int gpio_get_acpi(const struct gpio_desc *desc, struct acpi_gpio *gpio)
|
||||
{
|
||||
struct dm_gpio_ops *ops;
|
||||
|
||||
memset(gpio, '\0', sizeof(*gpio));
|
||||
if (!dm_gpio_is_valid(desc)) {
|
||||
/* Indicate that the GPIO is not valid */
|
||||
gpio->pin_count = 0;
|
||||
gpio->pins[0] = 0;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ops = gpio_get_ops(desc->dev);
|
||||
if (!ops->get_acpi)
|
||||
return -ENOSYS;
|
||||
|
||||
return ops->get_acpi(desc, gpio);
|
||||
}
|
||||
#endif
|
||||
|
||||
int gpio_claim_vector(const int *gpio_num_array, const char *fmt)
|
||||
{
|
||||
int i, ret;
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include <pch.h>
|
||||
#include <pci.h>
|
||||
#include <syscon.h>
|
||||
#include <acpi/acpi_device.h>
|
||||
#include <asm/cpu.h>
|
||||
#include <asm/gpio.h>
|
||||
#include <asm/intel_pinctrl.h>
|
||||
|
@ -19,12 +20,15 @@
|
|||
#include <asm/io.h>
|
||||
#include <asm/pci.h>
|
||||
#include <asm/arch/gpio.h>
|
||||
#include <dm/acpi.h>
|
||||
#include <dt-bindings/gpio/x86-gpio.h>
|
||||
|
||||
static int intel_gpio_direction_input(struct udevice *dev, uint offset)
|
||||
{
|
||||
struct udevice *pinctrl = dev_get_parent(dev);
|
||||
uint config_offset = intel_pinctrl_get_config_reg_addr(pinctrl, offset);
|
||||
uint config_offset;
|
||||
|
||||
config_offset = intel_pinctrl_get_config_reg_offset(pinctrl, offset);
|
||||
|
||||
pcr_clrsetbits32(pinctrl, config_offset,
|
||||
PAD_CFG0_MODE_MASK | PAD_CFG0_TX_STATE |
|
||||
|
@ -38,7 +42,9 @@ static int intel_gpio_direction_output(struct udevice *dev, uint offset,
|
|||
int value)
|
||||
{
|
||||
struct udevice *pinctrl = dev_get_parent(dev);
|
||||
uint config_offset = intel_pinctrl_get_config_reg_addr(pinctrl, offset);
|
||||
uint config_offset;
|
||||
|
||||
config_offset = intel_pinctrl_get_config_reg_offset(pinctrl, offset);
|
||||
|
||||
pcr_clrsetbits32(pinctrl, config_offset,
|
||||
PAD_CFG0_MODE_MASK | PAD_CFG0_RX_STATE |
|
||||
|
@ -68,10 +74,13 @@ static int intel_gpio_get_value(struct udevice *dev, uint offset)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int intel_gpio_set_value(struct udevice *dev, unsigned offset, int value)
|
||||
static int intel_gpio_set_value(struct udevice *dev, unsigned int offset,
|
||||
int value)
|
||||
{
|
||||
struct udevice *pinctrl = dev_get_parent(dev);
|
||||
uint config_offset = intel_pinctrl_get_config_reg_addr(pinctrl, offset);
|
||||
uint config_offset;
|
||||
|
||||
config_offset = intel_pinctrl_get_config_reg_offset(pinctrl, offset);
|
||||
|
||||
pcr_clrsetbits32(pinctrl, config_offset, PAD_CFG0_TX_STATE,
|
||||
value ? PAD_CFG0_TX_STATE : 0);
|
||||
|
@ -121,6 +130,35 @@ static int intel_gpio_xlate(struct udevice *orig_dev, struct gpio_desc *desc,
|
|||
return 0;
|
||||
}
|
||||
|
||||
#if CONFIG_IS_ENABLED(ACPIGEN)
|
||||
static int intel_gpio_get_acpi(const struct gpio_desc *desc,
|
||||
struct acpi_gpio *gpio)
|
||||
{
|
||||
struct udevice *pinctrl;
|
||||
int ret;
|
||||
|
||||
if (!dm_gpio_is_valid(desc))
|
||||
return -ENOENT;
|
||||
pinctrl = dev_get_parent(desc->dev);
|
||||
|
||||
memset(gpio, '\0', sizeof(*gpio));
|
||||
|
||||
gpio->type = ACPI_GPIO_TYPE_IO;
|
||||
gpio->pull = ACPI_GPIO_PULL_DEFAULT;
|
||||
gpio->io_restrict = ACPI_GPIO_IO_RESTRICT_OUTPUT;
|
||||
gpio->polarity = ACPI_GPIO_ACTIVE_HIGH;
|
||||
gpio->pin_count = 1;
|
||||
gpio->pins[0] = intel_pinctrl_get_acpi_pin(pinctrl, desc->offset);
|
||||
gpio->pin0_addr = intel_pinctrl_get_config_reg_addr(pinctrl,
|
||||
desc->offset);
|
||||
ret = acpi_get_path(pinctrl, gpio->resource, sizeof(gpio->resource));
|
||||
if (ret)
|
||||
return log_msg_ret("resource", ret);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int intel_gpio_probe(struct udevice *dev)
|
||||
{
|
||||
return 0;
|
||||
|
@ -145,6 +183,9 @@ static const struct dm_gpio_ops gpio_intel_ops = {
|
|||
.set_value = intel_gpio_set_value,
|
||||
.get_function = intel_gpio_get_function,
|
||||
.xlate = intel_gpio_xlate,
|
||||
#if CONFIG_IS_ENABLED(ACPIGEN)
|
||||
.get_acpi = intel_gpio_get_acpi,
|
||||
#endif
|
||||
};
|
||||
|
||||
static const struct udevice_id intel_intel_gpio_ids[] = {
|
||||
|
|
|
@ -8,7 +8,9 @@
|
|||
#include <fdtdec.h>
|
||||
#include <log.h>
|
||||
#include <malloc.h>
|
||||
#include <acpi/acpi_device.h>
|
||||
#include <asm/gpio.h>
|
||||
#include <dm/acpi.h>
|
||||
#include <dm/device_compat.h>
|
||||
#include <dm/lists.h>
|
||||
#include <dm/of.h>
|
||||
|
@ -197,6 +199,63 @@ static int sb_gpio_get_dir_flags(struct udevice *dev, unsigned int offset,
|
|||
return 0;
|
||||
}
|
||||
|
||||
#if CONFIG_IS_ENABLED(ACPIGEN)
|
||||
static int sb_gpio_get_acpi(const struct gpio_desc *desc,
|
||||
struct acpi_gpio *gpio)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Note that gpio_get_acpi() zeroes *gpio before calling here */
|
||||
gpio->pin_count = 1;
|
||||
gpio->pins[0] = desc->offset;
|
||||
ret = acpi_device_scope(desc->dev, gpio->resource,
|
||||
sizeof(gpio->resource));
|
||||
if (ret)
|
||||
return log_ret(ret);
|
||||
|
||||
/* All of these values are just used for testing */
|
||||
if (desc->flags & GPIOD_ACTIVE_LOW) {
|
||||
gpio->pin0_addr = 0x80012 + desc->offset;
|
||||
gpio->type = ACPI_GPIO_TYPE_INTERRUPT;
|
||||
gpio->pull = ACPI_GPIO_PULL_DOWN;
|
||||
gpio->interrupt_debounce_timeout = 4321;
|
||||
|
||||
/* We use the GpioInt part */
|
||||
gpio->irq.pin = desc->offset;
|
||||
gpio->irq.polarity = ACPI_IRQ_ACTIVE_BOTH;
|
||||
gpio->irq.shared = ACPI_IRQ_SHARED;
|
||||
gpio->irq.wake = ACPI_IRQ_WAKE;
|
||||
|
||||
/* The GpioIo part is only used for testing */
|
||||
gpio->polarity = ACPI_GPIO_ACTIVE_LOW;
|
||||
} else {
|
||||
gpio->pin0_addr = 0xc00dc + desc->offset;
|
||||
gpio->type = ACPI_GPIO_TYPE_IO;
|
||||
gpio->pull = ACPI_GPIO_PULL_UP;
|
||||
gpio->interrupt_debounce_timeout = 0;
|
||||
|
||||
/* The GpioInt part is not used */
|
||||
|
||||
/* We use the GpioIo part */
|
||||
gpio->output_drive_strength = 1234;
|
||||
gpio->io_shared = true;
|
||||
gpio->io_restrict = ACPI_GPIO_IO_RESTRICT_INPUT;
|
||||
gpio->polarity = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sb_gpio_get_name(const struct udevice *dev, char *out_name)
|
||||
{
|
||||
return acpi_copy_name(out_name, "GPIO");
|
||||
}
|
||||
|
||||
struct acpi_ops gpio_sandbox_acpi_ops = {
|
||||
.get_name = sb_gpio_get_name,
|
||||
};
|
||||
#endif /* ACPIGEN */
|
||||
|
||||
static const struct dm_gpio_ops gpio_sandbox_ops = {
|
||||
.direction_input = sb_gpio_direction_input,
|
||||
.direction_output = sb_gpio_direction_output,
|
||||
|
@ -206,6 +265,9 @@ static const struct dm_gpio_ops gpio_sandbox_ops = {
|
|||
.xlate = sb_gpio_xlate,
|
||||
.set_dir_flags = sb_gpio_set_dir_flags,
|
||||
.get_dir_flags = sb_gpio_get_dir_flags,
|
||||
#if CONFIG_IS_ENABLED(ACPIGEN)
|
||||
.get_acpi = sb_gpio_get_acpi,
|
||||
#endif
|
||||
};
|
||||
|
||||
static int sandbox_gpio_ofdata_to_platdata(struct udevice *dev)
|
||||
|
@ -252,6 +314,7 @@ U_BOOT_DRIVER(sandbox_gpio) = {
|
|||
.probe = gpio_sandbox_probe,
|
||||
.remove = gpio_sandbox_remove,
|
||||
.ops = &gpio_sandbox_ops,
|
||||
ACPI_OPS_PTR(&gpio_sandbox_acpi_ops)
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER_ALIAS(sandbox_gpio, sandbox_gpio_alias)
|
||||
|
@ -421,6 +484,13 @@ static int sb_pinctrl_get_pin_muxing(struct udevice *dev,
|
|||
return 0;
|
||||
}
|
||||
|
||||
#if CONFIG_IS_ENABLED(ACPIGEN)
|
||||
static int sb_pinctrl_get_name(const struct udevice *dev, char *out_name)
|
||||
{
|
||||
return acpi_copy_name(out_name, "PINC");
|
||||
}
|
||||
#endif
|
||||
|
||||
static int sandbox_pinctrl_probe(struct udevice *dev)
|
||||
{
|
||||
struct sb_pinctrl_priv *priv = dev_get_priv(dev);
|
||||
|
@ -436,6 +506,12 @@ static struct pinctrl_ops sandbox_pinctrl_gpio_ops = {
|
|||
.get_pin_muxing = sb_pinctrl_get_pin_muxing,
|
||||
};
|
||||
|
||||
#if CONFIG_IS_ENABLED(ACPIGEN)
|
||||
struct acpi_ops pinctrl_sandbox_acpi_ops = {
|
||||
.get_name = sb_pinctrl_get_name,
|
||||
};
|
||||
#endif
|
||||
|
||||
static const struct udevice_id sandbox_pinctrl_gpio_match[] = {
|
||||
{ .compatible = "sandbox,pinctrl-gpio" },
|
||||
{ /* sentinel */ }
|
||||
|
@ -449,4 +525,5 @@ U_BOOT_DRIVER(sandbox_pinctrl_gpio) = {
|
|||
.bind = dm_scan_fdt_dev,
|
||||
.probe = sandbox_pinctrl_probe,
|
||||
.priv_auto_alloc_size = sizeof(struct sb_pinctrl_priv),
|
||||
ACPI_OPS_PTR(&pinctrl_sandbox_acpi_ops)
|
||||
};
|
||||
|
|
|
@ -160,9 +160,9 @@ static int dw_i2c_calc_timing(struct dw_i2c *priv, enum i2c_speed_mode mode,
|
|||
min_tlow_cnt = calc_counts(ic_clk, info->min_scl_lowtime_ns);
|
||||
min_thigh_cnt = calc_counts(ic_clk, info->min_scl_hightime_ns);
|
||||
|
||||
debug("dw_i2c: period %d rise %d fall %d tlow %d thigh %d spk %d\n",
|
||||
period_cnt, rise_cnt, fall_cnt, min_tlow_cnt, min_thigh_cnt,
|
||||
spk_cnt);
|
||||
debug("dw_i2c: mode %d, ic_clk %d, speed %d, period %d rise %d fall %d tlow %d thigh %d spk %d\n",
|
||||
mode, ic_clk, info->speed, period_cnt, rise_cnt, fall_cnt,
|
||||
min_tlow_cnt, min_thigh_cnt, spk_cnt);
|
||||
|
||||
/*
|
||||
* Back-solve for hcnt and lcnt according to the following equations:
|
||||
|
@ -174,7 +174,7 @@ static int dw_i2c_calc_timing(struct dw_i2c *priv, enum i2c_speed_mode mode,
|
|||
|
||||
if (hcnt < 0 || lcnt < 0) {
|
||||
debug("dw_i2c: bad counts. hcnt = %d lcnt = %d\n", hcnt, lcnt);
|
||||
return -EINVAL;
|
||||
return log_msg_ret("counts", -EINVAL);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -333,6 +333,32 @@ static int _dw_i2c_set_bus_speed(struct dw_i2c *priv, struct i2c_regs *i2c_base,
|
|||
/* Restore back i2c now speed set */
|
||||
if (ena == IC_ENABLE_0B)
|
||||
dw_i2c_enable(i2c_base, true);
|
||||
if (priv)
|
||||
priv->config = config;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dw_i2c_gen_speed_config(const struct udevice *dev, int speed_hz,
|
||||
struct dw_i2c_speed_config *config)
|
||||
{
|
||||
struct dw_i2c *priv = dev_get_priv(dev);
|
||||
ulong rate;
|
||||
int ret;
|
||||
|
||||
#if CONFIG_IS_ENABLED(CLK)
|
||||
rate = clk_get_rate(&priv->clk);
|
||||
if (IS_ERR_VALUE(rate))
|
||||
return log_msg_ret("clk", -EINVAL);
|
||||
#else
|
||||
rate = IC_CLK;
|
||||
#endif
|
||||
|
||||
ret = calc_bus_speed(priv, priv->regs, speed_hz, rate, config);
|
||||
if (ret)
|
||||
printf("%s: ret=%d\n", __func__, ret);
|
||||
if (ret)
|
||||
return log_msg_ret("calc_bus_speed", ret);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -713,7 +739,7 @@ static int designware_i2c_set_bus_speed(struct udevice *bus, unsigned int speed)
|
|||
#if CONFIG_IS_ENABLED(CLK)
|
||||
rate = clk_get_rate(&i2c->clk);
|
||||
if (IS_ERR_VALUE(rate))
|
||||
return -EINVAL;
|
||||
return log_ret(-EINVAL);
|
||||
#else
|
||||
rate = IC_CLK;
|
||||
#endif
|
||||
|
|
|
@ -205,6 +205,7 @@ struct dw_i2c {
|
|||
#if CONFIG_IS_ENABLED(CLK)
|
||||
struct clk clk;
|
||||
#endif
|
||||
struct dw_i2c_speed_config config;
|
||||
};
|
||||
|
||||
extern const struct dm_i2c_ops designware_i2c_ops;
|
||||
|
@ -213,4 +214,18 @@ int designware_i2c_probe(struct udevice *bus);
|
|||
int designware_i2c_remove(struct udevice *dev);
|
||||
int designware_i2c_ofdata_to_platdata(struct udevice *bus);
|
||||
|
||||
/**
|
||||
* dw_i2c_gen_speed_config() - Calculate config info from requested speed
|
||||
*
|
||||
* Calculate the speed config from the given @speed_hz and return it so that
|
||||
* it can be incorporated in ACPI tables
|
||||
*
|
||||
* @dev: I2C bus to check
|
||||
* @speed_hz: Requested speed in Hz
|
||||
* @config: Returns config to use for that speed
|
||||
* @return 0 if OK, -ve on error
|
||||
*/
|
||||
int dw_i2c_gen_speed_config(const struct udevice *dev, int speed_hz,
|
||||
struct dw_i2c_speed_config *config);
|
||||
|
||||
#endif /* __DW_I2C_H_ */
|
||||
|
|
|
@ -9,7 +9,12 @@
|
|||
#include <dm.h>
|
||||
#include <log.h>
|
||||
#include <spl.h>
|
||||
#include <acpi/acpigen.h>
|
||||
#include <acpi/acpi_device.h>
|
||||
#include <asm/lpss.h>
|
||||
#include <dm/acpi.h>
|
||||
#include <dm/device-internal.h>
|
||||
#include <dm/uclass-internal.h>
|
||||
#include "designware_i2c.h"
|
||||
|
||||
enum {
|
||||
|
@ -87,6 +92,9 @@ static int designware_i2c_pci_bind(struct udevice *dev)
|
|||
{
|
||||
char name[20];
|
||||
|
||||
if (dev_of_valid(dev))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Create a unique device name for PCI type devices
|
||||
* ToDo:
|
||||
|
@ -100,13 +108,98 @@ static int designware_i2c_pci_bind(struct udevice *dev)
|
|||
* be possible. We cannot use static data in drivers since they may be
|
||||
* used in SPL or before relocation.
|
||||
*/
|
||||
dev->req_seq = gd->arch.dw_i2c_num_cards++;
|
||||
dev->req_seq = uclass_find_next_free_req_seq(UCLASS_I2C);
|
||||
sprintf(name, "i2c_designware#%u", dev->req_seq);
|
||||
device_set_name(dev, name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Write ACPI object to describe speed configuration.
|
||||
*
|
||||
* ACPI Object: Name ("xxxx", Package () { scl_lcnt, scl_hcnt, sda_hold }
|
||||
*
|
||||
* SSCN: I2C_SPEED_STANDARD
|
||||
* FMCN: I2C_SPEED_FAST
|
||||
* FPCN: I2C_SPEED_FAST_PLUS
|
||||
* HSCN: I2C_SPEED_HIGH
|
||||
*/
|
||||
static void dw_i2c_acpi_write_speed_config(struct acpi_ctx *ctx,
|
||||
struct dw_i2c_speed_config *config)
|
||||
{
|
||||
switch (config->speed_mode) {
|
||||
case IC_SPEED_MODE_HIGH:
|
||||
acpigen_write_name(ctx, "HSCN");
|
||||
break;
|
||||
case IC_SPEED_MODE_FAST_PLUS:
|
||||
acpigen_write_name(ctx, "FPCN");
|
||||
break;
|
||||
case IC_SPEED_MODE_FAST:
|
||||
acpigen_write_name(ctx, "FMCN");
|
||||
break;
|
||||
case IC_SPEED_MODE_STANDARD:
|
||||
default:
|
||||
acpigen_write_name(ctx, "SSCN");
|
||||
}
|
||||
|
||||
/* Package () { scl_lcnt, scl_hcnt, sda_hold } */
|
||||
acpigen_write_package(ctx, 3);
|
||||
acpigen_write_word(ctx, config->scl_hcnt);
|
||||
acpigen_write_word(ctx, config->scl_lcnt);
|
||||
acpigen_write_dword(ctx, config->sda_hold);
|
||||
acpigen_pop_len(ctx);
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate I2C timing information into the SSDT for the OS driver to consume,
|
||||
* optionally applying override values provided by the caller.
|
||||
*/
|
||||
static int dw_i2c_acpi_fill_ssdt(const struct udevice *dev,
|
||||
struct acpi_ctx *ctx)
|
||||
{
|
||||
struct dw_i2c_speed_config config;
|
||||
char path[ACPI_PATH_MAX];
|
||||
u32 speeds[4];
|
||||
uint speed;
|
||||
int size;
|
||||
int ret;
|
||||
|
||||
/* If no device-tree node, ignore this since we assume it isn't used */
|
||||
if (!dev_of_valid(dev))
|
||||
return 0;
|
||||
|
||||
ret = acpi_device_path(dev, path, sizeof(path));
|
||||
if (ret)
|
||||
return log_msg_ret("path", ret);
|
||||
|
||||
size = dev_read_size(dev, "i2c,speeds");
|
||||
if (size < 0)
|
||||
return log_msg_ret("i2c,speeds", -EINVAL);
|
||||
|
||||
size /= sizeof(u32);
|
||||
if (size > ARRAY_SIZE(speeds))
|
||||
return log_msg_ret("array", -E2BIG);
|
||||
|
||||
ret = dev_read_u32_array(dev, "i2c,speeds", speeds, size);
|
||||
if (ret)
|
||||
return log_msg_ret("read", -E2BIG);
|
||||
|
||||
speed = dev_read_u32_default(dev, "clock-frequency", 100000);
|
||||
acpigen_write_scope(ctx, path);
|
||||
ret = dw_i2c_gen_speed_config(dev, speed, &config);
|
||||
if (ret)
|
||||
return log_msg_ret("config", ret);
|
||||
dw_i2c_acpi_write_speed_config(ctx, &config);
|
||||
acpigen_pop_len(ctx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct acpi_ops dw_i2c_acpi_ops = {
|
||||
.fill_ssdt = dw_i2c_acpi_fill_ssdt,
|
||||
};
|
||||
|
||||
static const struct udevice_id designware_i2c_pci_ids[] = {
|
||||
{ .compatible = "snps,designware-i2c-pci" },
|
||||
{ .compatible = "intel,apl-i2c", .data = INTEL_APL },
|
||||
|
@ -124,6 +217,7 @@ U_BOOT_DRIVER(i2c_designware_pci) = {
|
|||
.remove = designware_i2c_remove,
|
||||
.flags = DM_FLAG_OS_PREPARE,
|
||||
.ops = &designware_i2c_ops,
|
||||
ACPI_OPS_PTR(&dw_i2c_acpi_ops)
|
||||
};
|
||||
|
||||
static struct pci_device_id designware_pci_supported[] = {
|
||||
|
|
|
@ -458,7 +458,7 @@ int i2c_set_chip_offset_len(struct udevice *dev, uint offset_len)
|
|||
struct dm_i2c_chip *chip = dev_get_parent_platdata(dev);
|
||||
|
||||
if (offset_len > I2C_MAX_OFFSET_LEN)
|
||||
return -EINVAL;
|
||||
return log_ret(-EINVAL);
|
||||
chip->offset_len = offset_len;
|
||||
|
||||
return 0;
|
||||
|
@ -625,7 +625,7 @@ int i2c_chip_ofdata_to_platdata(struct udevice *dev, struct dm_i2c_chip *chip)
|
|||
if (addr == -1) {
|
||||
debug("%s: I2C Node '%s' has no 'reg' property %s\n", __func__,
|
||||
dev_read_name(dev), dev->name);
|
||||
return -EINVAL;
|
||||
return log_ret(-EINVAL);
|
||||
}
|
||||
chip->chip_addr = addr;
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include <i2c.h>
|
||||
#include <log.h>
|
||||
#include <asm/test.h>
|
||||
#include <dm/acpi.h>
|
||||
#include <dm/lists.h>
|
||||
#include <dm/device-internal.h>
|
||||
|
||||
|
|
|
@ -243,10 +243,10 @@ config NUVOTON_NCT6102D
|
|||
in the Nuvoton Super IO chips on X86 platforms.
|
||||
|
||||
config P2SB
|
||||
bool "Intel Primary-to-Sideband Bus"
|
||||
bool "Intel Primary to Sideband Bridge"
|
||||
depends on X86 || SANDBOX
|
||||
help
|
||||
This enables support for the Intel Primary-to-Sideband bus,
|
||||
This enables support for the Intel Primary to Sideband Bridge,
|
||||
abbreviated to P2SB. The P2SB is used to access various peripherals
|
||||
such as eSPI, GPIO, through memory-mapped I/O in a large chunk of PCI
|
||||
space. The space is segmented into different channels and peripherals
|
||||
|
@ -256,20 +256,20 @@ config P2SB
|
|||
devices - see pcr_readl(), etc.
|
||||
|
||||
config SPL_P2SB
|
||||
bool "Intel Primary-to-Sideband Bus in SPL"
|
||||
bool "Intel Primary to Sideband Bridge in SPL"
|
||||
depends on SPL && (X86 || SANDBOX)
|
||||
help
|
||||
The Primary-to-Sideband bus is used to access various peripherals
|
||||
The Primary to Sideband Bridge is used to access various peripherals
|
||||
through memory-mapped I/O in a large chunk of PCI space. The space is
|
||||
segmented into different channels and peripherals are accessed by
|
||||
device-specific means within those channels. Devices should be added
|
||||
in the device tree as subnodes of the p2sb.
|
||||
|
||||
config TPL_P2SB
|
||||
bool "Intel Primary-to-Sideband Bus in TPL"
|
||||
bool "Intel Primary to Sideband Bridge in TPL"
|
||||
depends on TPL && (X86 || SANDBOX)
|
||||
help
|
||||
The Primary-to-Sideband bus is used to access various peripherals
|
||||
The Primary to Sideband Bridge is used to access various peripherals
|
||||
through memory-mapped I/O in a large chunk of PCI space. The space is
|
||||
segmented into different channels and peripherals are accessed by
|
||||
device-specific means within those channels. Devices should be added
|
||||
|
|
|
@ -152,8 +152,6 @@ int irq_request(struct udevice *dev, struct irq *irq)
|
|||
const struct irq_ops *ops;
|
||||
|
||||
log_debug("(dev=%p, irq=%p)\n", dev, irq);
|
||||
if (!irq)
|
||||
return 0;
|
||||
ops = irq_get_ops(dev);
|
||||
|
||||
irq->dev = dev;
|
||||
|
@ -170,11 +168,27 @@ int irq_first_device_type(enum irq_dev_t type, struct udevice **devp)
|
|||
|
||||
ret = uclass_first_device_drvdata(UCLASS_IRQ, type, devp);
|
||||
if (ret)
|
||||
return log_msg_ret("find", ret);
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if CONFIG_IS_ENABLED(ACPIGEN)
|
||||
int irq_get_acpi(const struct irq *irq, struct acpi_irq *acpi_irq)
|
||||
{
|
||||
struct irq_ops *ops;
|
||||
|
||||
if (!irq_is_valid(irq))
|
||||
return -EINVAL;
|
||||
|
||||
ops = irq_get_ops(irq->dev);
|
||||
if (!ops->get_acpi)
|
||||
return -ENOSYS;
|
||||
|
||||
return ops->get_acpi(irq, acpi_irq);
|
||||
}
|
||||
#endif
|
||||
|
||||
UCLASS_DRIVER(irq) = {
|
||||
.id = UCLASS_IRQ,
|
||||
.name = "irq",
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <irq.h>
|
||||
#include <acpi/acpi_device.h>
|
||||
#include <asm/test.h>
|
||||
|
||||
/**
|
||||
|
@ -73,6 +74,18 @@ static int sandbox_irq_of_xlate(struct irq *irq,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static __maybe_unused int sandbox_get_acpi(const struct irq *irq,
|
||||
struct acpi_irq *acpi_irq)
|
||||
{
|
||||
acpi_irq->pin = irq->id;
|
||||
acpi_irq->mode = ACPI_IRQ_LEVEL_TRIGGERED;
|
||||
acpi_irq->polarity = ACPI_IRQ_ACTIVE_HIGH;
|
||||
acpi_irq->shared = ACPI_IRQ_SHARED;
|
||||
acpi_irq->wake = ACPI_IRQ_WAKE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct irq_ops sandbox_irq_ops = {
|
||||
.route_pmc_gpio_gpe = sandbox_route_pmc_gpio_gpe,
|
||||
.set_polarity = sandbox_set_polarity,
|
||||
|
@ -80,6 +93,9 @@ static const struct irq_ops sandbox_irq_ops = {
|
|||
.restore_polarities = sandbox_restore_polarities,
|
||||
.read_and_clear = sandbox_irq_read_and_clear,
|
||||
.of_xlate = sandbox_irq_of_xlate,
|
||||
#if CONFIG_IS_ENABLED(ACPIGEN)
|
||||
.get_acpi = sandbox_get_acpi,
|
||||
#endif
|
||||
};
|
||||
|
||||
static const struct udevice_id sandbox_irq_ids[] = {
|
||||
|
|
|
@ -18,7 +18,17 @@
|
|||
|
||||
#define PCR_COMMON_IOSF_1_0 1
|
||||
|
||||
static void *_pcr_reg_address(struct udevice *dev, uint offset)
|
||||
int p2sb_set_hide(struct udevice *dev, bool hide)
|
||||
{
|
||||
struct p2sb_ops *ops = p2sb_get_ops(dev);
|
||||
|
||||
if (!ops->set_hide)
|
||||
return -ENOSYS;
|
||||
|
||||
return ops->set_hide(dev, hide);
|
||||
}
|
||||
|
||||
void *pcr_reg_address(struct udevice *dev, uint offset)
|
||||
{
|
||||
struct p2sb_child_platdata *pplat = dev_get_parent_platdata(dev);
|
||||
struct udevice *p2sb = dev_get_parent(dev);
|
||||
|
@ -55,7 +65,7 @@ uint pcr_read32(struct udevice *dev, uint offset)
|
|||
/* Ensure the PCR offset is correctly aligned */
|
||||
assert(IS_ALIGNED(offset, sizeof(uint32_t)));
|
||||
|
||||
ptr = _pcr_reg_address(dev, offset);
|
||||
ptr = pcr_reg_address(dev, offset);
|
||||
val = readl(ptr);
|
||||
unmap_sysmem(ptr);
|
||||
|
||||
|
@ -67,7 +77,7 @@ uint pcr_read16(struct udevice *dev, uint offset)
|
|||
/* Ensure the PCR offset is correctly aligned */
|
||||
check_pcr_offset_align(offset, sizeof(uint16_t));
|
||||
|
||||
return readw(_pcr_reg_address(dev, offset));
|
||||
return readw(pcr_reg_address(dev, offset));
|
||||
}
|
||||
|
||||
uint pcr_read8(struct udevice *dev, uint offset)
|
||||
|
@ -75,7 +85,7 @@ uint pcr_read8(struct udevice *dev, uint offset)
|
|||
/* Ensure the PCR offset is correctly aligned */
|
||||
check_pcr_offset_align(offset, sizeof(uint8_t));
|
||||
|
||||
return readb(_pcr_reg_address(dev, offset));
|
||||
return readb(pcr_reg_address(dev, offset));
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -86,7 +96,7 @@ uint pcr_read8(struct udevice *dev, uint offset)
|
|||
*/
|
||||
static void write_completion(struct udevice *dev, uint offset)
|
||||
{
|
||||
readl(_pcr_reg_address(dev, ALIGN_DOWN(offset, sizeof(uint32_t))));
|
||||
readl(pcr_reg_address(dev, ALIGN_DOWN(offset, sizeof(uint32_t))));
|
||||
}
|
||||
|
||||
void pcr_write32(struct udevice *dev, uint offset, uint indata)
|
||||
|
@ -94,7 +104,7 @@ void pcr_write32(struct udevice *dev, uint offset, uint indata)
|
|||
/* Ensure the PCR offset is correctly aligned */
|
||||
assert(IS_ALIGNED(offset, sizeof(indata)));
|
||||
|
||||
writel(indata, _pcr_reg_address(dev, offset));
|
||||
writel(indata, pcr_reg_address(dev, offset));
|
||||
/* Ensure the writes complete */
|
||||
write_completion(dev, offset);
|
||||
}
|
||||
|
@ -104,7 +114,7 @@ void pcr_write16(struct udevice *dev, uint offset, uint indata)
|
|||
/* Ensure the PCR offset is correctly aligned */
|
||||
check_pcr_offset_align(offset, sizeof(uint16_t));
|
||||
|
||||
writew(indata, _pcr_reg_address(dev, offset));
|
||||
writew(indata, pcr_reg_address(dev, offset));
|
||||
/* Ensure the writes complete */
|
||||
write_completion(dev, offset);
|
||||
}
|
||||
|
@ -114,7 +124,7 @@ void pcr_write8(struct udevice *dev, uint offset, uint indata)
|
|||
/* Ensure the PCR offset is correctly aligned */
|
||||
check_pcr_offset_align(offset, sizeof(uint8_t));
|
||||
|
||||
writeb(indata, _pcr_reg_address(dev, offset));
|
||||
writeb(indata, pcr_reg_address(dev, offset));
|
||||
/* Ensure the writes complete */
|
||||
write_completion(dev, offset);
|
||||
}
|
||||
|
|
|
@ -7,10 +7,15 @@
|
|||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <errno.h>
|
||||
#include <log.h>
|
||||
#include <malloc.h>
|
||||
#include <mapmem.h>
|
||||
#include <sdhci.h>
|
||||
#include <asm/pci.h>
|
||||
#include <acpi/acpigen.h>
|
||||
#include <acpi/acpi_device.h>
|
||||
#include <acpi/acpi_dp.h>
|
||||
#include <asm-generic/gpio.h>
|
||||
#include <dm/acpi.h>
|
||||
|
||||
struct pci_mmc_plat {
|
||||
struct mmc_config cfg;
|
||||
|
@ -20,6 +25,7 @@ struct pci_mmc_plat {
|
|||
struct pci_mmc_priv {
|
||||
struct sdhci_host host;
|
||||
void *base;
|
||||
struct gpio_desc cd_gpio;
|
||||
};
|
||||
|
||||
static int pci_mmc_probe(struct udevice *dev)
|
||||
|
@ -44,6 +50,15 @@ static int pci_mmc_probe(struct udevice *dev)
|
|||
return sdhci_probe(dev);
|
||||
}
|
||||
|
||||
static int pci_mmc_ofdata_to_platdata(struct udevice *dev)
|
||||
{
|
||||
struct pci_mmc_priv *priv = dev_get_priv(dev);
|
||||
|
||||
gpio_request_by_name(dev, "cd-gpios", 0, &priv->cd_gpio, GPIOD_IS_IN);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pci_mmc_bind(struct udevice *dev)
|
||||
{
|
||||
struct pci_mmc_plat *plat = dev_get_platdata(dev);
|
||||
|
@ -51,14 +66,75 @@ static int pci_mmc_bind(struct udevice *dev)
|
|||
return sdhci_bind(dev, &plat->mmc, &plat->cfg);
|
||||
}
|
||||
|
||||
static int pci_mmc_acpi_fill_ssdt(const struct udevice *dev,
|
||||
struct acpi_ctx *ctx)
|
||||
{
|
||||
struct pci_mmc_priv *priv = dev_get_priv(dev);
|
||||
char path[ACPI_PATH_MAX];
|
||||
struct acpi_gpio gpio;
|
||||
struct acpi_dp *dp;
|
||||
int ret;
|
||||
|
||||
if (!dev_of_valid(dev))
|
||||
return 0;
|
||||
|
||||
ret = gpio_get_acpi(&priv->cd_gpio, &gpio);
|
||||
if (ret)
|
||||
return log_msg_ret("gpio", ret);
|
||||
gpio.type = ACPI_GPIO_TYPE_INTERRUPT;
|
||||
gpio.pull = ACPI_GPIO_PULL_NONE;
|
||||
gpio.irq.mode = ACPI_IRQ_EDGE_TRIGGERED;
|
||||
gpio.irq.polarity = ACPI_IRQ_ACTIVE_BOTH;
|
||||
gpio.irq.shared = ACPI_IRQ_SHARED;
|
||||
gpio.irq.wake = ACPI_IRQ_WAKE;
|
||||
gpio.interrupt_debounce_timeout = 10000; /* 100ms */
|
||||
|
||||
/* Use device path as the Scope for the SSDT */
|
||||
ret = acpi_device_path(dev, path, sizeof(path));
|
||||
if (ret)
|
||||
return log_msg_ret("path", ret);
|
||||
acpigen_write_scope(ctx, path);
|
||||
acpigen_write_name(ctx, "_CRS");
|
||||
|
||||
/* Write GpioInt() as default (if set) or custom from devicetree */
|
||||
acpigen_write_resourcetemplate_header(ctx);
|
||||
acpi_device_write_gpio(ctx, &gpio);
|
||||
acpigen_write_resourcetemplate_footer(ctx);
|
||||
|
||||
/* Bind the cd-gpio name to the GpioInt() resource */
|
||||
dp = acpi_dp_new_table("_DSD");
|
||||
if (!dp)
|
||||
return -ENOMEM;
|
||||
acpi_dp_add_gpio(dp, "cd-gpio", path, 0, 0, 1);
|
||||
ret = acpi_dp_write(ctx, dp);
|
||||
if (ret)
|
||||
return log_msg_ret("cd", ret);
|
||||
|
||||
acpigen_pop_len(ctx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct acpi_ops pci_mmc_acpi_ops = {
|
||||
.fill_ssdt = pci_mmc_acpi_fill_ssdt,
|
||||
};
|
||||
|
||||
static const struct udevice_id pci_mmc_match[] = {
|
||||
{ .compatible = "intel,apl-sd" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(pci_mmc) = {
|
||||
.name = "pci_mmc",
|
||||
.id = UCLASS_MMC,
|
||||
.of_match = pci_mmc_match,
|
||||
.bind = pci_mmc_bind,
|
||||
.ofdata_to_platdata = pci_mmc_ofdata_to_platdata,
|
||||
.probe = pci_mmc_probe,
|
||||
.ops = &sdhci_ops,
|
||||
.priv_auto_alloc_size = sizeof(struct pci_mmc_priv),
|
||||
.platdata_auto_alloc_size = sizeof(struct pci_mmc_plat),
|
||||
ACPI_OPS_PTR(&pci_mmc_acpi_ops)
|
||||
};
|
||||
|
||||
static struct pci_device_id mmc_supported[] = {
|
||||
|
|
|
@ -15,6 +15,18 @@ config INTEL_PINCTRL_IOSTANDBY
|
|||
bool
|
||||
default y
|
||||
|
||||
config INTEL_PINCTRL_MULTI_ACPI_DEVICES
|
||||
bool
|
||||
default y
|
||||
help
|
||||
Enable this if the pinctrl devices are modelled as multiple,
|
||||
separate ACPI devices in the ACPI tables. If enabled, the ACPI
|
||||
devices match the U-Boot pinctrl devices and the pin 'offset' is
|
||||
relatove to a particular pinctrl device. If disabled, there is a
|
||||
single ACPI pinctrl device which includes all U-Boot pinctrl devices
|
||||
and the pin 'offset' is in effect a global pin number.
|
||||
|
||||
|
||||
config PINCTRL_INTEL_APL
|
||||
bool "Support Intel Apollo Lake (APL)"
|
||||
help
|
||||
|
|
|
@ -394,7 +394,7 @@ static int pinctrl_configure_pad(struct udevice *dev,
|
|||
return 0;
|
||||
}
|
||||
|
||||
u32 intel_pinctrl_get_config_reg_addr(struct udevice *dev, uint offset)
|
||||
u32 intel_pinctrl_get_config_reg_offset(struct udevice *dev, uint offset)
|
||||
{
|
||||
struct intel_pinctrl_priv *priv = dev_get_priv(dev);
|
||||
const struct pad_community *comm = priv->comm;
|
||||
|
@ -407,9 +407,16 @@ u32 intel_pinctrl_get_config_reg_addr(struct udevice *dev, uint offset)
|
|||
return config_offset;
|
||||
}
|
||||
|
||||
u32 intel_pinctrl_get_config_reg_addr(struct udevice *dev, uint offset)
|
||||
{
|
||||
uint config_offset = intel_pinctrl_get_config_reg_offset(dev, offset);
|
||||
|
||||
return (u32)(ulong)pcr_reg_address(dev, config_offset);
|
||||
}
|
||||
|
||||
u32 intel_pinctrl_get_config_reg(struct udevice *dev, uint offset)
|
||||
{
|
||||
uint config_offset = intel_pinctrl_get_config_reg_addr(dev, offset);
|
||||
uint config_offset = intel_pinctrl_get_config_reg_offset(dev, offset);
|
||||
|
||||
return pcr_read32(dev, config_offset);
|
||||
}
|
||||
|
@ -420,6 +427,8 @@ int intel_pinctrl_get_acpi_pin(struct udevice *dev, uint offset)
|
|||
const struct pad_community *comm = priv->comm;
|
||||
int group;
|
||||
|
||||
if (IS_ENABLED(CONFIG_INTEL_PINCTRL_MULTI_ACPI_DEVICES))
|
||||
return offset;
|
||||
group = pinctrl_group_index(comm, offset);
|
||||
|
||||
/* If pad base is not set then use GPIO number as ACPI pin number */
|
||||
|
@ -610,15 +619,11 @@ int intel_pinctrl_ofdata_to_platdata(struct udevice *dev,
|
|||
{
|
||||
struct p2sb_child_platdata *pplat = dev_get_parent_platdata(dev);
|
||||
struct intel_pinctrl_priv *priv = dev_get_priv(dev);
|
||||
int ret;
|
||||
|
||||
if (!comm) {
|
||||
log_err("Cannot find community for pid %d\n", pplat->pid);
|
||||
return -EDOM;
|
||||
}
|
||||
ret = irq_first_device_type(X86_IRQT_ITSS, &priv->itss);
|
||||
if (ret)
|
||||
return log_msg_ret("Cannot find ITSS", ret);
|
||||
priv->comm = comm;
|
||||
priv->num_cfgs = num_cfgs;
|
||||
|
||||
|
@ -628,8 +633,12 @@ int intel_pinctrl_ofdata_to_platdata(struct udevice *dev,
|
|||
int intel_pinctrl_probe(struct udevice *dev)
|
||||
{
|
||||
struct intel_pinctrl_priv *priv = dev_get_priv(dev);
|
||||
int ret;
|
||||
|
||||
priv->itss_pol_cfg = true;
|
||||
ret = irq_first_device_type(X86_IRQT_ITSS, &priv->itss);
|
||||
if (ret)
|
||||
return log_msg_ret("Cannot find ITSS", ret);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -75,7 +75,6 @@ static const struct pad_community apl_gpio_communities[] = {
|
|||
.gpi_smi_en_reg_0 = GPI_SMI_EN_0,
|
||||
.max_pads_per_group = GPIO_MAX_NUM_PER_GROUP,
|
||||
.name = "GPIO_GPE_N",
|
||||
.acpi_path = "\\_SB.GPO0",
|
||||
.reset_map = rst_map,
|
||||
.num_reset_vals = ARRAY_SIZE(rst_map),
|
||||
.groups = apl_community_n_groups,
|
||||
|
@ -94,7 +93,6 @@ static const struct pad_community apl_gpio_communities[] = {
|
|||
.gpi_smi_en_reg_0 = GPI_SMI_EN_0,
|
||||
.max_pads_per_group = GPIO_MAX_NUM_PER_GROUP,
|
||||
.name = "GPIO_GPE_NW",
|
||||
.acpi_path = "\\_SB.GPO1",
|
||||
.reset_map = rst_map,
|
||||
.num_reset_vals = ARRAY_SIZE(rst_map),
|
||||
.groups = apl_community_nw_groups,
|
||||
|
@ -113,7 +111,6 @@ static const struct pad_community apl_gpio_communities[] = {
|
|||
.gpi_smi_en_reg_0 = GPI_SMI_EN_0,
|
||||
.max_pads_per_group = GPIO_MAX_NUM_PER_GROUP,
|
||||
.name = "GPIO_GPE_W",
|
||||
.acpi_path = "\\_SB.GPO2",
|
||||
.reset_map = rst_map,
|
||||
.num_reset_vals = ARRAY_SIZE(rst_map),
|
||||
.groups = apl_community_w_groups,
|
||||
|
@ -132,7 +129,6 @@ static const struct pad_community apl_gpio_communities[] = {
|
|||
.gpi_smi_en_reg_0 = GPI_SMI_EN_0,
|
||||
.max_pads_per_group = GPIO_MAX_NUM_PER_GROUP,
|
||||
.name = "GPIO_GPE_SW",
|
||||
.acpi_path = "\\_SB.GPO3",
|
||||
.reset_map = rst_map,
|
||||
.num_reset_vals = ARRAY_SIZE(rst_map),
|
||||
.groups = apl_community_sw_groups,
|
||||
|
|
|
@ -15,15 +15,6 @@
|
|||
#include <asm/io.h>
|
||||
#include <power/acpi_pmc.h>
|
||||
|
||||
enum {
|
||||
PM1_STS = 0x00,
|
||||
PM1_EN = 0x02,
|
||||
PM1_CNT = 0x04,
|
||||
|
||||
GPE0_STS = 0x20,
|
||||
GPE0_EN = 0x30,
|
||||
};
|
||||
|
||||
struct tco_regs {
|
||||
u32 tco_rld;
|
||||
u32 tco_sts;
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include <i2c.h>
|
||||
#include <rtc.h>
|
||||
#include <asm/rtc.h>
|
||||
#include <dm/acpi.h>
|
||||
|
||||
#define REG_COUNT 0x80
|
||||
|
||||
|
@ -67,6 +68,17 @@ static int sandbox_rtc_write8(struct udevice *dev, unsigned int reg, int val)
|
|||
return dm_i2c_reg_write(dev, reg, val);
|
||||
}
|
||||
|
||||
#if CONFIG_IS_ENABLED(ACPIGEN)
|
||||
static int sandbox_rtc_get_name(const struct udevice *dev, char *out_name)
|
||||
{
|
||||
return acpi_copy_name(out_name, "RTCC");
|
||||
}
|
||||
|
||||
struct acpi_ops sandbox_rtc_acpi_ops = {
|
||||
.get_name = sandbox_rtc_get_name,
|
||||
};
|
||||
#endif
|
||||
|
||||
static const struct rtc_ops sandbox_rtc_ops = {
|
||||
.get = sandbox_rtc_get,
|
||||
.set = sandbox_rtc_set,
|
||||
|
@ -85,4 +97,5 @@ U_BOOT_DRIVER(rtc_sandbox) = {
|
|||
.id = UCLASS_RTC,
|
||||
.of_match = sandbox_rtc_ids,
|
||||
.ops = &sandbox_rtc_ops,
|
||||
ACPI_OPS_PTR(&sandbox_rtc_acpi_ops)
|
||||
};
|
||||
|
|
|
@ -40,6 +40,15 @@ config I2S_SAMSUNG
|
|||
option provides an implementation for sound_init() and
|
||||
sound_play().
|
||||
|
||||
config SOUND_DA7219
|
||||
bool "Dialog Semiconductor audio codec"
|
||||
depends on SOUND
|
||||
help
|
||||
The DA7219 is an ultra-low-power audio codec with Advanced Accessory
|
||||
Detection (AAD). This driver only supports generation of ACPI tables.
|
||||
It does not support sound output or any of the other codec
|
||||
features.
|
||||
|
||||
config SOUND_I8254
|
||||
bool "Intel i8254 timer / beeper"
|
||||
depends on SOUND
|
||||
|
@ -104,6 +113,15 @@ config SOUND_MAX98095
|
|||
audio data and I2C for codec control. At present it only works
|
||||
with the Samsung I2S driver.
|
||||
|
||||
config SOUND_MAX98357A
|
||||
bool "Support Maxim max98357a audio codec"
|
||||
depends on PCI
|
||||
help
|
||||
Enable the max98357a audio codec. This is connected on PCI for
|
||||
audio data codec control. This is currently only capable of providing
|
||||
ACPI information. A full driver (with sound in U-Boot) is currently
|
||||
not available.
|
||||
|
||||
config SOUND_RT5677
|
||||
bool "Support Realtek RT5677 audio codec"
|
||||
depends on SOUND
|
||||
|
|
|
@ -7,6 +7,7 @@ obj-$(CONFIG_SOUND) += sound.o
|
|||
obj-$(CONFIG_SOUND) += codec-uclass.o
|
||||
obj-$(CONFIG_SOUND) += i2s-uclass.o
|
||||
obj-$(CONFIG_SOUND) += sound-uclass.o
|
||||
obj-$(CONFIG_SOUND_DA7219) += da7219.o
|
||||
obj-$(CONFIG_I2S_SAMSUNG) += samsung-i2s.o
|
||||
obj-$(CONFIG_SOUND_SANDBOX) += sandbox.o
|
||||
obj-$(CONFIG_I2S_ROCKCHIP) += rockchip_i2s.o rockchip_sound.o
|
||||
|
@ -16,6 +17,7 @@ obj-$(CONFIG_SOUND_WM8994) += wm8994.o
|
|||
obj-$(CONFIG_SOUND_MAX98088) += max98088.o maxim_codec.o
|
||||
obj-$(CONFIG_SOUND_MAX98090) += max98090.o maxim_codec.o
|
||||
obj-$(CONFIG_SOUND_MAX98095) += max98095.o maxim_codec.o
|
||||
obj-$(CONFIG_SOUND_MAX98357A) += max98357a.o
|
||||
obj-$(CONFIG_SOUND_INTEL_HDA) += hda_codec.o
|
||||
obj-$(CONFIG_SOUND_I8254) += i8254_beep.o
|
||||
obj-$(CONFIG_SOUND_RT5677) += rt5677.o
|
||||
|
|
190
drivers/sound/da7219.c
Normal file
190
drivers/sound/da7219.c
Normal file
|
@ -0,0 +1,190 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* ACPI driver for DA7219 codec
|
||||
*
|
||||
* Copyright 2019 Google LLC
|
||||
* Parts taken from coreboot
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <i2c.h>
|
||||
#include <irq.h>
|
||||
#include <log.h>
|
||||
#include <acpi/acpigen.h>
|
||||
#include <acpi/acpi_device.h>
|
||||
#include <acpi/acpi_dp.h>
|
||||
#ifdef CONFIG_X86
|
||||
#include <asm/acpi_nhlt.h>
|
||||
#endif
|
||||
#include <asm-generic/gpio.h>
|
||||
#include <dt-bindings/sound/nhlt.h>
|
||||
#include <dm/acpi.h>
|
||||
|
||||
#define DA7219_ACPI_HID "DLGS7219"
|
||||
|
||||
static int da7219_acpi_fill_ssdt(const struct udevice *dev,
|
||||
struct acpi_ctx *ctx)
|
||||
{
|
||||
char scope[ACPI_PATH_MAX];
|
||||
char name[ACPI_NAME_MAX];
|
||||
struct acpi_dp *dsd, *aad;
|
||||
ofnode node;
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
ret = acpi_device_scope(dev, scope, sizeof(scope));
|
||||
if (ret)
|
||||
return log_msg_ret("scope", ret);
|
||||
ret = acpi_get_name(dev, name);
|
||||
if (ret)
|
||||
return log_msg_ret("name", ret);
|
||||
|
||||
/* Device */
|
||||
acpigen_write_scope(ctx, scope);
|
||||
acpigen_write_device(ctx, name);
|
||||
acpigen_write_name_string(ctx, "_HID", DA7219_ACPI_HID);
|
||||
acpigen_write_name_integer(ctx, "_UID", 1);
|
||||
acpigen_write_name_string(ctx, "_DDN",
|
||||
dev_read_string(dev, "acpi,ddn"));
|
||||
acpigen_write_name_integer(ctx, "_S0W", 4);
|
||||
acpigen_write_sta(ctx, acpi_device_status(dev));
|
||||
|
||||
/* Resources */
|
||||
acpigen_write_name(ctx, "_CRS");
|
||||
acpigen_write_resourcetemplate_header(ctx);
|
||||
ret = acpi_device_write_i2c_dev(ctx, dev);
|
||||
if (ret)
|
||||
return log_msg_ret("i2c", ret);
|
||||
|
||||
/* Use either Interrupt() or GpioInt() */
|
||||
ret = acpi_device_write_interrupt_or_gpio(ctx, (struct udevice *)dev,
|
||||
"req-gpios");
|
||||
if (ret)
|
||||
return log_msg_ret("irq_gpio", ret);
|
||||
acpigen_write_resourcetemplate_footer(ctx);
|
||||
|
||||
/* AAD Child Device Properties */
|
||||
aad = acpi_dp_new_table("DAAD");
|
||||
if (!aad)
|
||||
return log_msg_ret("aad", -ENOMEM);
|
||||
|
||||
node = ofnode_find_subnode(dev_ofnode(dev), "da7219_aad");
|
||||
if (!ofnode_valid(node))
|
||||
return log_msg_ret("da7219_aad", -EINVAL);
|
||||
acpi_dp_ofnode_copy_int(node, aad, "dlg,btn-cfg");
|
||||
acpi_dp_ofnode_copy_int(node, aad, "dlg,mic-det-thr");
|
||||
acpi_dp_ofnode_copy_int(node, aad, "dlg,jack-ins-deb");
|
||||
acpi_dp_ofnode_copy_str(node, aad, "dlg,jack-det-rate");
|
||||
acpi_dp_ofnode_copy_int(node, aad, "dlg,jack-rem-deb");
|
||||
acpi_dp_ofnode_copy_int(node, aad, "dlg,a-d-btn-thr");
|
||||
acpi_dp_ofnode_copy_int(node, aad, "dlg,d-b-btn-thr");
|
||||
acpi_dp_ofnode_copy_int(node, aad, "dlg,b-c-btn-thr");
|
||||
acpi_dp_ofnode_copy_int(node, aad, "dlg,c-mic-btn-thr");
|
||||
acpi_dp_ofnode_copy_int(node, aad, "dlg,btn-avg");
|
||||
acpi_dp_ofnode_copy_int(node, aad, "dlg,adc-1bit-rpt");
|
||||
if (!ofnode_read_u32(node, "dlg,micbias-pulse-lvl", &val)) {
|
||||
acpi_dp_ofnode_copy_int(node, aad, "dlg,micbias-pulse-lvl");
|
||||
acpi_dp_ofnode_copy_int(node, aad, "dlg,micbias-pulse-time");
|
||||
}
|
||||
|
||||
/* DA7219 Properties */
|
||||
dsd = acpi_dp_new_table("_DSD");
|
||||
if (!dsd)
|
||||
return log_msg_ret("dsd", -ENOMEM);
|
||||
acpi_dp_dev_copy_int(dev, dsd, "dlg,micbias-lvl");
|
||||
acpi_dp_dev_copy_str(dev, dsd, "dlg,mic-amp-in-sel");
|
||||
acpi_dp_dev_copy_str(dev, dsd, "dlg,mclk-name");
|
||||
acpi_dp_add_child(dsd, "da7219_aad", aad);
|
||||
|
||||
/* Write Device Property Hierarchy */
|
||||
acpi_dp_write(ctx, dsd);
|
||||
|
||||
acpigen_pop_len(ctx); /* Device */
|
||||
acpigen_pop_len(ctx); /* Scope */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* For now only X86 boards support NHLT */
|
||||
#ifdef CONFIG_X86
|
||||
static const struct nhlt_format_config da7219_formats[] = {
|
||||
/* 48 KHz 24-bits per sample. */
|
||||
{
|
||||
.num_channels = 2,
|
||||
.sample_freq_khz = 48,
|
||||
.container_bits_per_sample = 32,
|
||||
.valid_bits_per_sample = 24,
|
||||
.settings_file = "dialog-2ch-48khz-24b.dat",
|
||||
},
|
||||
};
|
||||
|
||||
static const struct nhlt_tdm_config tdm_config = {
|
||||
.virtual_slot = 0,
|
||||
.config_type = NHLT_TDM_BASIC,
|
||||
};
|
||||
|
||||
static const struct nhlt_endp_descriptor da7219_descriptors[] = {
|
||||
/* Render Endpoint */
|
||||
{
|
||||
.link = NHLT_LINK_SSP,
|
||||
.device = NHLT_SSP_DEV_I2S,
|
||||
.direction = NHLT_DIR_RENDER,
|
||||
.vid = NHLT_VID,
|
||||
.did = NHLT_DID_SSP,
|
||||
.cfg = &tdm_config,
|
||||
.cfg_size = sizeof(tdm_config),
|
||||
.formats = da7219_formats,
|
||||
.num_formats = ARRAY_SIZE(da7219_formats),
|
||||
},
|
||||
/* Capture Endpoint */
|
||||
{
|
||||
.link = NHLT_LINK_SSP,
|
||||
.device = NHLT_SSP_DEV_I2S,
|
||||
.direction = NHLT_DIR_CAPTURE,
|
||||
.vid = NHLT_VID,
|
||||
.did = NHLT_DID_SSP,
|
||||
.cfg = &tdm_config,
|
||||
.cfg_size = sizeof(tdm_config),
|
||||
.formats = da7219_formats,
|
||||
.num_formats = ARRAY_SIZE(da7219_formats),
|
||||
},
|
||||
};
|
||||
|
||||
static int da7219_acpi_setup_nhlt(const struct udevice *dev,
|
||||
struct acpi_ctx *ctx)
|
||||
{
|
||||
u32 hwlink;
|
||||
int ret;
|
||||
|
||||
if (dev_read_u32(dev, "acpi,audio-link", &hwlink))
|
||||
return log_msg_ret("link", -EINVAL);
|
||||
|
||||
/* Virtual bus id of SSP links are the hardware port ids proper. */
|
||||
ret = nhlt_add_ssp_endpoints(ctx->nhlt, hwlink, da7219_descriptors,
|
||||
ARRAY_SIZE(da7219_descriptors));
|
||||
if (ret)
|
||||
return log_msg_ret("add", ret);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
struct acpi_ops da7219_acpi_ops = {
|
||||
.fill_ssdt = da7219_acpi_fill_ssdt,
|
||||
#ifdef CONFIG_X86
|
||||
.setup_nhlt = da7219_acpi_setup_nhlt,
|
||||
#endif
|
||||
};
|
||||
|
||||
static const struct udevice_id da7219_ids[] = {
|
||||
{ .compatible = "dlg,da7219" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(da7219) = {
|
||||
.name = "da7219",
|
||||
.id = UCLASS_MISC,
|
||||
.of_match = da7219_ids,
|
||||
ACPI_OPS_PTR(&da7219_acpi_ops)
|
||||
};
|
161
drivers/sound/max98357a.c
Normal file
161
drivers/sound/max98357a.c
Normal file
|
@ -0,0 +1,161 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* max98357a.c -- MAX98357A Audio driver
|
||||
*
|
||||
* Copyright 2019 Google LLC
|
||||
* Parts taken from coreboot
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <audio_codec.h>
|
||||
#include <dm.h>
|
||||
#include <log.h>
|
||||
#include <sound.h>
|
||||
#include <acpi/acpigen.h>
|
||||
#include <acpi/acpi_device.h>
|
||||
#include <acpi/acpi_dp.h>
|
||||
#include <asm-generic/gpio.h>
|
||||
#ifdef CONFIG_X86
|
||||
#include <asm/acpi_nhlt.h>
|
||||
#endif
|
||||
#include <dt-bindings/sound/nhlt.h>
|
||||
#include <dm/acpi.h>
|
||||
|
||||
struct max98357a_priv {
|
||||
struct gpio_desc sdmode_gpio;
|
||||
};
|
||||
|
||||
static int max98357a_ofdata_to_platdata(struct udevice *dev)
|
||||
{
|
||||
struct max98357a_priv *priv = dev_get_priv(dev);
|
||||
int ret;
|
||||
|
||||
ret = gpio_request_by_name(dev, "sdmode-gpios", 0, &priv->sdmode_gpio,
|
||||
GPIOD_IS_IN);
|
||||
if (ret)
|
||||
return log_msg_ret("gpio", ret);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int max98357a_acpi_fill_ssdt(const struct udevice *dev,
|
||||
struct acpi_ctx *ctx)
|
||||
{
|
||||
struct max98357a_priv *priv = dev_get_priv(dev);
|
||||
char scope[ACPI_PATH_MAX];
|
||||
char name[ACPI_NAME_MAX];
|
||||
char path[ACPI_PATH_MAX];
|
||||
struct acpi_dp *dp;
|
||||
int ret;
|
||||
|
||||
ret = acpi_device_scope(dev, scope, sizeof(scope));
|
||||
if (ret)
|
||||
return log_msg_ret("scope", ret);
|
||||
ret = acpi_get_name(dev, name);
|
||||
if (ret)
|
||||
return log_msg_ret("name", ret);
|
||||
|
||||
/* Device */
|
||||
acpigen_write_scope(ctx, scope);
|
||||
acpigen_write_device(ctx, name);
|
||||
acpigen_write_name_string(ctx, "_HID",
|
||||
dev_read_string(dev, "acpi,hid"));
|
||||
acpigen_write_name_integer(ctx, "_UID", 0);
|
||||
acpigen_write_name_string(ctx, "_DDN",
|
||||
dev_read_string(dev, "acpi,ddn"));
|
||||
acpigen_write_sta(ctx, acpi_device_status(dev));
|
||||
|
||||
/* Resources */
|
||||
acpigen_write_name(ctx, "_CRS");
|
||||
acpigen_write_resourcetemplate_header(ctx);
|
||||
ret = acpi_device_write_gpio_desc(ctx, &priv->sdmode_gpio);
|
||||
if (ret)
|
||||
return log_msg_ret("gpio", ret);
|
||||
acpigen_write_resourcetemplate_footer(ctx);
|
||||
|
||||
/* _DSD for devicetree properties */
|
||||
/* This points to the first pin in the first gpio entry in _CRS */
|
||||
ret = acpi_device_path(dev, path, sizeof(path));
|
||||
if (ret)
|
||||
return log_msg_ret("path", ret);
|
||||
dp = acpi_dp_new_table("_DSD");
|
||||
acpi_dp_add_gpio(dp, "sdmode-gpio", path, 0, 0,
|
||||
priv->sdmode_gpio.flags & GPIOD_ACTIVE_LOW ?
|
||||
ACPI_IRQ_ACTIVE_LOW : ACPI_IRQ_ACTIVE_HIGH);
|
||||
acpi_dp_add_integer(dp, "sdmode-delay",
|
||||
dev_read_u32_default(dev, "sdmode-delay", 0));
|
||||
acpi_dp_write(ctx, dp);
|
||||
|
||||
acpigen_pop_len(ctx); /* Device */
|
||||
acpigen_pop_len(ctx); /* Scope */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* For now only X86 boards support NHLT */
|
||||
#ifdef CONFIG_X86
|
||||
static const struct nhlt_format_config max98357a_formats[] = {
|
||||
/* 48 KHz 24-bits per sample. */
|
||||
{
|
||||
.num_channels = 2,
|
||||
.sample_freq_khz = 48,
|
||||
.container_bits_per_sample = 32,
|
||||
.valid_bits_per_sample = 24,
|
||||
.settings_file = "max98357-render-2ch-48khz-24b.dat",
|
||||
},
|
||||
};
|
||||
|
||||
static const struct nhlt_endp_descriptor max98357a_descriptors[] = {
|
||||
{
|
||||
.link = NHLT_LINK_SSP,
|
||||
.device = NHLT_SSP_DEV_I2S,
|
||||
.direction = NHLT_DIR_RENDER,
|
||||
.vid = NHLT_VID,
|
||||
.did = NHLT_DID_SSP,
|
||||
.formats = max98357a_formats,
|
||||
.num_formats = ARRAY_SIZE(max98357a_formats),
|
||||
},
|
||||
};
|
||||
|
||||
static int max98357a_acpi_setup_nhlt(const struct udevice *dev,
|
||||
struct acpi_ctx *ctx)
|
||||
{
|
||||
u32 hwlink;
|
||||
int ret;
|
||||
|
||||
if (dev_read_u32(dev, "acpi,audio-link", &hwlink))
|
||||
return log_msg_ret("link", -EINVAL);
|
||||
|
||||
/* Virtual bus id of SSP links are the hardware port ids proper. */
|
||||
ret = nhlt_add_ssp_endpoints(ctx->nhlt, hwlink, max98357a_descriptors,
|
||||
ARRAY_SIZE(max98357a_descriptors));
|
||||
if (ret)
|
||||
return log_msg_ret("add", ret);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
struct acpi_ops max98357a_acpi_ops = {
|
||||
.fill_ssdt = max98357a_acpi_fill_ssdt,
|
||||
#ifdef CONFIG_X86
|
||||
.setup_nhlt = max98357a_acpi_setup_nhlt,
|
||||
#endif
|
||||
};
|
||||
|
||||
static const struct audio_codec_ops max98357a_ops = {
|
||||
};
|
||||
|
||||
static const struct udevice_id max98357a_ids[] = {
|
||||
{ .compatible = "maxim,max98357a" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(max98357a) = {
|
||||
.name = "max98357a",
|
||||
.id = UCLASS_AUDIO_CODEC,
|
||||
.of_match = max98357a_ids,
|
||||
.ofdata_to_platdata = max98357a_ofdata_to_platdata,
|
||||
.ops = &max98357a_ops,
|
||||
ACPI_OPS_PTR(&max98357a_acpi_ops)
|
||||
};
|
|
@ -21,6 +21,7 @@
|
|||
#include <linux/errno.h>
|
||||
#include <asm/spi.h>
|
||||
#include <asm/state.h>
|
||||
#include <dm/acpi.h>
|
||||
#include <dm/device-internal.h>
|
||||
|
||||
#ifndef CONFIG_SPI_IDLE_VAL
|
||||
|
|
406
include/acpi/acpi_device.h
Normal file
406
include/acpi/acpi_device.h
Normal file
|
@ -0,0 +1,406 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Generation of tables for particular device types
|
||||
*
|
||||
* Copyright 2019 Google LLC
|
||||
* Mostly taken from coreboot file of the same name
|
||||
*/
|
||||
|
||||
#ifndef __ACPI_DEVICE_H
|
||||
#define __ACPI_DEVICE_H
|
||||
|
||||
#include <i2c.h>
|
||||
#include <spi.h>
|
||||
#include <linux/bitops.h>
|
||||
|
||||
struct acpi_ctx;
|
||||
struct gpio_desc;
|
||||
struct irq;
|
||||
struct udevice;
|
||||
|
||||
/* ACPI descriptor values for common descriptors: SERIAL_BUS means I2C */
|
||||
#define ACPI_DESCRIPTOR_LARGE BIT(7)
|
||||
#define ACPI_DESCRIPTOR_REGISTER (ACPI_DESCRIPTOR_LARGE | 2)
|
||||
#define ACPI_DESCRIPTOR_INTERRUPT (ACPI_DESCRIPTOR_LARGE | 9)
|
||||
#define ACPI_DESCRIPTOR_GPIO (ACPI_DESCRIPTOR_LARGE | 12)
|
||||
#define ACPI_DESCRIPTOR_SERIAL_BUS (ACPI_DESCRIPTOR_LARGE | 14)
|
||||
|
||||
/* Length of a full path to an ACPI device */
|
||||
#define ACPI_PATH_MAX 30
|
||||
|
||||
/* Values that can be returned for ACPI device _STA method */
|
||||
enum acpi_dev_status {
|
||||
ACPI_DSTATUS_PRESENT = BIT(0),
|
||||
ACPI_DSTATUS_ENABLED = BIT(1),
|
||||
ACPI_DSTATUS_SHOW_IN_UI = BIT(2),
|
||||
ACPI_DSTATUS_OK = BIT(3),
|
||||
ACPI_DSTATUS_HAS_BATTERY = BIT(4),
|
||||
|
||||
ACPI_DSTATUS_ALL_OFF = 0,
|
||||
ACPI_DSTATUS_HIDDEN_ON = ACPI_DSTATUS_PRESENT | ACPI_DSTATUS_ENABLED |
|
||||
ACPI_DSTATUS_OK,
|
||||
ACPI_DSTATUS_ALL_ON = ACPI_DSTATUS_HIDDEN_ON |
|
||||
ACPI_DSTATUS_SHOW_IN_UI,
|
||||
};
|
||||
|
||||
/** enum acpi_irq_mode - edge/level trigger mode */
|
||||
enum acpi_irq_mode {
|
||||
ACPI_IRQ_EDGE_TRIGGERED,
|
||||
ACPI_IRQ_LEVEL_TRIGGERED,
|
||||
};
|
||||
|
||||
/**
|
||||
* enum acpi_irq_polarity - polarity of interrupt
|
||||
*
|
||||
* @ACPI_IRQ_ACTIVE_LOW - for ACPI_IRQ_EDGE_TRIGGERED this means falling edge
|
||||
* @ACPI_IRQ_ACTIVE_HIGH - for ACPI_IRQ_EDGE_TRIGGERED this means rising edge
|
||||
* @ACPI_IRQ_ACTIVE_BOTH - not meaningful for ACPI_IRQ_EDGE_TRIGGERED
|
||||
*/
|
||||
enum acpi_irq_polarity {
|
||||
ACPI_IRQ_ACTIVE_LOW,
|
||||
ACPI_IRQ_ACTIVE_HIGH,
|
||||
ACPI_IRQ_ACTIVE_BOTH,
|
||||
};
|
||||
|
||||
/**
|
||||
* enum acpi_irq_shared - whether interrupt is shared or not
|
||||
*
|
||||
* @ACPI_IRQ_EXCLUSIVE: only this device uses the interrupt
|
||||
* @ACPI_IRQ_SHARED: other devices may use this interrupt
|
||||
*/
|
||||
enum acpi_irq_shared {
|
||||
ACPI_IRQ_EXCLUSIVE,
|
||||
ACPI_IRQ_SHARED,
|
||||
};
|
||||
|
||||
/** enum acpi_irq_wake - indicates whether this interrupt can wake the device */
|
||||
enum acpi_irq_wake {
|
||||
ACPI_IRQ_NO_WAKE,
|
||||
ACPI_IRQ_WAKE,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct acpi_irq - representation of an ACPI interrupt
|
||||
*
|
||||
* @pin: ACPI pin that is monitored for the interrupt
|
||||
* @mode: Edge/level triggering
|
||||
* @polarity: Interrupt polarity
|
||||
* @shared: Whether interrupt is shared or not
|
||||
* @wake: Whether interrupt can wake the device from sleep
|
||||
*/
|
||||
struct acpi_irq {
|
||||
unsigned int pin;
|
||||
enum acpi_irq_mode mode;
|
||||
enum acpi_irq_polarity polarity;
|
||||
enum acpi_irq_shared shared;
|
||||
enum acpi_irq_wake wake;
|
||||
};
|
||||
|
||||
/**
|
||||
* enum acpi_gpio_type - type of the descriptor
|
||||
*
|
||||
* @ACPI_GPIO_TYPE_INTERRUPT: GpioInterrupt
|
||||
* @ACPI_GPIO_TYPE_IO: GpioIo
|
||||
*/
|
||||
enum acpi_gpio_type {
|
||||
ACPI_GPIO_TYPE_INTERRUPT,
|
||||
ACPI_GPIO_TYPE_IO,
|
||||
};
|
||||
|
||||
/**
|
||||
* enum acpi_gpio_pull - pull direction
|
||||
*
|
||||
* @ACPI_GPIO_PULL_DEFAULT: Use default value for pin
|
||||
* @ACPI_GPIO_PULL_UP: Pull up
|
||||
* @ACPI_GPIO_PULL_DOWN: Pull down
|
||||
* @ACPI_GPIO_PULL_NONE: No pullup/pulldown
|
||||
*/
|
||||
enum acpi_gpio_pull {
|
||||
ACPI_GPIO_PULL_DEFAULT,
|
||||
ACPI_GPIO_PULL_UP,
|
||||
ACPI_GPIO_PULL_DOWN,
|
||||
ACPI_GPIO_PULL_NONE,
|
||||
};
|
||||
|
||||
/**
|
||||
* enum acpi_gpio_io_restrict - controls input/output of pin
|
||||
*
|
||||
* @ACPI_GPIO_IO_RESTRICT_NONE: no restrictions
|
||||
* @ACPI_GPIO_IO_RESTRICT_INPUT: input only (no output)
|
||||
* @ACPI_GPIO_IO_RESTRICT_OUTPUT: output only (no input)
|
||||
* @ACPI_GPIO_IO_RESTRICT_PRESERVE: preserve settings when driver not active
|
||||
*/
|
||||
enum acpi_gpio_io_restrict {
|
||||
ACPI_GPIO_IO_RESTRICT_NONE,
|
||||
ACPI_GPIO_IO_RESTRICT_INPUT,
|
||||
ACPI_GPIO_IO_RESTRICT_OUTPUT,
|
||||
ACPI_GPIO_IO_RESTRICT_PRESERVE,
|
||||
};
|
||||
|
||||
/** enum acpi_gpio_polarity - controls the GPIO polarity */
|
||||
enum acpi_gpio_polarity {
|
||||
ACPI_GPIO_ACTIVE_HIGH = 0,
|
||||
ACPI_GPIO_ACTIVE_LOW = 1,
|
||||
};
|
||||
|
||||
#define ACPI_GPIO_REVISION_ID 1
|
||||
#define ACPI_GPIO_MAX_PINS 2
|
||||
|
||||
/**
|
||||
* struct acpi_gpio - representation of an ACPI GPIO
|
||||
*
|
||||
* @pin_count: Number of pins represented
|
||||
* @pins: List of pins
|
||||
* @pin0_addr: Address in memory of the control registers for pin 0. This is
|
||||
* used when generating ACPI tables
|
||||
* @type: GPIO type
|
||||
* @pull: Pullup/pulldown setting
|
||||
* @resource: Resource name for this GPIO controller
|
||||
* For GpioInt:
|
||||
* @interrupt_debounce_timeout: Debounce timeout in units of 10us
|
||||
* @irq: Interrupt
|
||||
*
|
||||
* For GpioIo:
|
||||
* @output_drive_strength: Drive strength in units of 10uA
|
||||
* @io_shared; true if GPIO is shared
|
||||
* @io_restrict: I/O restriction setting
|
||||
* @polarity: GPIO polarity
|
||||
*/
|
||||
struct acpi_gpio {
|
||||
int pin_count;
|
||||
u16 pins[ACPI_GPIO_MAX_PINS];
|
||||
ulong pin0_addr;
|
||||
|
||||
enum acpi_gpio_type type;
|
||||
enum acpi_gpio_pull pull;
|
||||
char resource[ACPI_PATH_MAX];
|
||||
|
||||
/* GpioInt */
|
||||
u16 interrupt_debounce_timeout;
|
||||
struct acpi_irq irq;
|
||||
|
||||
/* GpioIo */
|
||||
u16 output_drive_strength;
|
||||
bool io_shared;
|
||||
enum acpi_gpio_io_restrict io_restrict;
|
||||
enum acpi_gpio_polarity polarity;
|
||||
};
|
||||
|
||||
/* ACPI Descriptors for Serial Bus interfaces */
|
||||
#define ACPI_SERIAL_BUS_TYPE_I2C 1
|
||||
#define ACPI_SERIAL_BUS_TYPE_SPI 2
|
||||
#define ACPI_I2C_SERIAL_BUS_REVISION_ID 1 /* TODO: upgrade to 2 */
|
||||
#define ACPI_I2C_TYPE_SPECIFIC_REVISION_ID 1
|
||||
#define ACPI_SPI_SERIAL_BUS_REVISION_ID 1
|
||||
#define ACPI_SPI_TYPE_SPECIFIC_REVISION_ID 1
|
||||
|
||||
/**
|
||||
* struct acpi_i2c - representation of an ACPI I2C device
|
||||
*
|
||||
* @address: 7-bit or 10-bit I2C address
|
||||
* @mode_10bit: Which address size is used
|
||||
* @speed: Bus speed in Hz
|
||||
* @resource: Resource name for the I2C controller
|
||||
*/
|
||||
struct acpi_i2c {
|
||||
u16 address;
|
||||
enum i2c_address_mode mode_10bit;
|
||||
enum i2c_speed_rate speed;
|
||||
const char *resource;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct acpi_spi - representation of an ACPI SPI device
|
||||
*
|
||||
* @device_select: Chip select used by this device (typically 0)
|
||||
* @device_select_polarity: Polarity for the device
|
||||
* @wire_mode: Number of wires used for SPI
|
||||
* @speed: Bus speed in Hz
|
||||
* @data_bit_length: Word length for SPI (typically 8)
|
||||
* @clock_phase: Clock phase to capture data
|
||||
* @clock_polarity: Bus polarity
|
||||
* @resource: Resource name for the SPI controller
|
||||
*/
|
||||
struct acpi_spi {
|
||||
u16 device_select;
|
||||
enum spi_polarity device_select_polarity;
|
||||
enum spi_wire_mode wire_mode;
|
||||
unsigned int speed;
|
||||
u8 data_bit_length;
|
||||
enum spi_clock_phase clock_phase;
|
||||
enum spi_polarity clock_polarity;
|
||||
const char *resource;
|
||||
};
|
||||
|
||||
/**
|
||||
* acpi_device_path() - Get the full path to an ACPI device
|
||||
*
|
||||
* This gets the full path in the form XXXX.YYYY.ZZZZ where XXXX is the root
|
||||
* and ZZZZ is the device. All parent devices are added to the path.
|
||||
*
|
||||
* @dev: Device to check
|
||||
* @buf: Buffer to place the path in (should be ACPI_PATH_MAX long)
|
||||
* @maxlen: Size of buffer (typically ACPI_PATH_MAX)
|
||||
* @return 0 if OK, -ve on error
|
||||
*/
|
||||
int acpi_device_path(const struct udevice *dev, char *buf, int maxlen);
|
||||
|
||||
/**
|
||||
* acpi_device_scope() - Get the scope of an ACPI device
|
||||
*
|
||||
* This gets the scope which is the full path of the parent device, as per
|
||||
* acpi_device_path().
|
||||
*
|
||||
* @dev: Device to check
|
||||
* @buf: Buffer to place the path in (should be ACPI_PATH_MAX long)
|
||||
* @maxlen: Size of buffer (typically ACPI_PATH_MAX)
|
||||
* @return 0 if OK, -EINVAL if the device has no parent, other -ve on other
|
||||
* error
|
||||
*/
|
||||
int acpi_device_scope(const struct udevice *dev, char *scope, int maxlen);
|
||||
|
||||
/**
|
||||
* acpi_device_status() - Get the status of a device
|
||||
*
|
||||
* This currently just returns ACPI_DSTATUS_ALL_ON. It does not support
|
||||
* inactive or hidden devices.
|
||||
*
|
||||
* @dev: Device to check
|
||||
* @return device status, as ACPI_DSTATUS_...
|
||||
*/
|
||||
enum acpi_dev_status acpi_device_status(const struct udevice *dev);
|
||||
|
||||
/**
|
||||
* acpi_device_write_interrupt_irq() - Write an interrupt descriptor
|
||||
*
|
||||
* This writes an ACPI interrupt descriptor for the given interrupt, converting
|
||||
* fields as needed.
|
||||
*
|
||||
* @ctx: ACPI context pointer
|
||||
* @req_irq: Interrupt to output
|
||||
* @return IRQ pin number if OK, -ve on error
|
||||
*/
|
||||
int acpi_device_write_interrupt_irq(struct acpi_ctx *ctx,
|
||||
const struct irq *req_irq);
|
||||
|
||||
/**
|
||||
* acpi_device_write_gpio() - Write GpioIo() or GpioInt() descriptor
|
||||
*
|
||||
* @gpio: GPIO information to write
|
||||
* @return GPIO pin number of first GPIO if OK, -ve on error
|
||||
*/
|
||||
int acpi_device_write_gpio(struct acpi_ctx *ctx, const struct acpi_gpio *gpio);
|
||||
|
||||
/**
|
||||
* acpi_device_write_gpio_desc() - Write a GPIO to ACPI
|
||||
*
|
||||
* This creates a GPIO descriptor for a GPIO, including information ACPI needs
|
||||
* to use it.
|
||||
*
|
||||
* @ctx: ACPI context pointer
|
||||
* @desc: GPIO to write
|
||||
* @return 0 if OK, -ve on error
|
||||
*/
|
||||
int acpi_device_write_gpio_desc(struct acpi_ctx *ctx,
|
||||
const struct gpio_desc *desc);
|
||||
|
||||
/**
|
||||
* acpi_device_write_interrupt_or_gpio() - Write interrupt or GPIO to ACPI
|
||||
*
|
||||
* This reads an interrupt from the device tree "interrupts-extended" property,
|
||||
* if available. If not it reads the first GPIO with the name @prop.
|
||||
*
|
||||
* If an interrupt is found, an ACPI interrupt descriptor is written to the ACPI
|
||||
* output. If not, but if a GPIO is found, a GPIO descriptor is written.
|
||||
*
|
||||
* @return irq or GPIO pin number if OK, -ve if neither an interrupt nor a GPIO
|
||||
* could be found, or some other error occurred
|
||||
*/
|
||||
int acpi_device_write_interrupt_or_gpio(struct acpi_ctx *ctx,
|
||||
struct udevice *dev, const char *prop);
|
||||
|
||||
/**
|
||||
* acpi_device_write_i2c_dev() - Write an I2C device to ACPI
|
||||
*
|
||||
* This creates a I2cSerialBus descriptor for an I2C device, including
|
||||
* information ACPI needs to use it.
|
||||
*
|
||||
* @ctx: ACPI context pointer
|
||||
* @dev: I2C device to write
|
||||
* @return I2C address of device if OK, -ve on error
|
||||
*/
|
||||
int acpi_device_write_i2c_dev(struct acpi_ctx *ctx, const struct udevice *dev);
|
||||
|
||||
/**
|
||||
* acpi_device_write_spi_dev() - Write a SPI device to ACPI
|
||||
*
|
||||
* This writes a serial bus descriptor for the SPI device so that ACPI can use
|
||||
* it
|
||||
*
|
||||
* @ctx: ACPI context pointer
|
||||
* @dev: SPI device to write
|
||||
* @return 0 if OK, -ve on error
|
||||
*/
|
||||
int acpi_device_write_spi_dev(struct acpi_ctx *ctx, const struct udevice *dev);
|
||||
|
||||
/**
|
||||
* acpi_device_add_power_res() - Add a basic PowerResource block for a device
|
||||
*
|
||||
* This includes GPIOs to control enable, reset and stop operation of the
|
||||
* device. Each GPIO is optional, but at least one must be provided.
|
||||
* This can be applied to any device that has power control, so is fairly
|
||||
* generic.
|
||||
*
|
||||
* Reset - Put the device into / take the device out of reset.
|
||||
* Enable - Enable / disable power to device.
|
||||
* Stop - Stop / start operation of device.
|
||||
*
|
||||
* @ctx: ACPI context pointer
|
||||
* @tx_state_val: Mask to use to toggle the TX state on the GPIO pin, e,g.
|
||||
* PAD_CFG0_TX_STATE
|
||||
* @dw0_read: Name to use to read dw0, e.g. "\\_SB.GPC0"
|
||||
* @dw0_write: Name to use to read dw0, e.g. "\\_SB.SPC0"
|
||||
* @reset_gpio: GPIO used to take device out of reset or to put it into reset
|
||||
* @reset_delay_ms: Delay to be inserted after device is taken out of reset
|
||||
* (_ON method delay)
|
||||
* @reset_off_delay_ms: Delay to be inserted after device is put into reset
|
||||
* (_OFF method delay)
|
||||
* @enable_gpio: GPIO used to enable device
|
||||
* @enable_delay_ms: Delay to be inserted after device is enabled
|
||||
* @enable_off_delay_ms: Delay to be inserted after device is disabled
|
||||
* (_OFF method delay)
|
||||
* @stop_gpio: GPIO used to stop operation of device
|
||||
* @stop_delay_ms: Delay to be inserted after disabling stop (_ON method delay)
|
||||
* @stop_off_delay_ms: Delay to be inserted after enabling stop.
|
||||
* (_OFF method delay)
|
||||
*
|
||||
* @return 0 if OK, -ve if at least one GPIO is not provided
|
||||
*/
|
||||
int acpi_device_add_power_res(struct acpi_ctx *ctx, u32 tx_state_val,
|
||||
const char *dw0_read, const char *dw0_write,
|
||||
const struct gpio_desc *reset_gpio,
|
||||
uint reset_delay_ms, uint reset_off_delay_ms,
|
||||
const struct gpio_desc *enable_gpio,
|
||||
uint enable_delay_ms, uint enable_off_delay_ms,
|
||||
const struct gpio_desc *stop_gpio,
|
||||
uint stop_delay_ms, uint stop_off_delay_ms);
|
||||
|
||||
/**
|
||||
* acpi_device_infer_name() - Infer the name from its uclass or parent
|
||||
*
|
||||
* Many ACPI devices have a standard name that can be inferred from the uclass
|
||||
* they are in, or the uclass of their parent. These rules are implemented in
|
||||
* this function. It attempts to produce a name for a device based on these
|
||||
* rules.
|
||||
*
|
||||
* NOTE: This currently supports only x86 devices. Feel free to enhance it for
|
||||
* other architectures as needed.
|
||||
*
|
||||
* @dev: Device to check
|
||||
* @out_name: Place to put the name (must hold ACPI_NAME_MAX bytes)
|
||||
* @return 0 if a name was found, -ENOENT if not found, -ENXIO if the device
|
||||
* sequence number could not be determined
|
||||
*/
|
||||
int acpi_device_infer_name(const struct udevice *dev, char *out_name);
|
||||
|
||||
#endif
|
287
include/acpi/acpi_dp.h
Normal file
287
include/acpi/acpi_dp.h
Normal file
|
@ -0,0 +1,287 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Device properties, a temporary data structure for adding to ACPI code
|
||||
*
|
||||
* Copyright 2019 Google LLC
|
||||
* Mostly taken from coreboot file acpi_device.h
|
||||
*/
|
||||
|
||||
#ifndef __ACPI_DP_H
|
||||
#define __ACPI_DP_H
|
||||
|
||||
struct acpi_ctx;
|
||||
|
||||
#include <acpi/acpi_device.h>
|
||||
|
||||
/*
|
||||
* Writing Device Properties objects via _DSD
|
||||
*
|
||||
* This is described in ACPI 6.3 section 6.2.5
|
||||
*
|
||||
* This provides a structure to handle nested device-specific data which ends
|
||||
* up in a _DSD table.
|
||||
*
|
||||
* https://www.kernel.org/doc/html/latest/firmware-guide/acpi/DSD-properties-rules.html
|
||||
* https://uefi.org/sites/default/files/resources/_DSD-device-properties-UUID.pdf
|
||||
* https://uefi.org/sites/default/files/resources/_DSD-hierarchical-data-extension-UUID-v1.1.pdf
|
||||
*
|
||||
* The Device Property Hierarchy can be multiple levels deep with multiple
|
||||
* children possible in each level. In order to support this flexibility
|
||||
* the device property hierarchy must be built up before being written out.
|
||||
*
|
||||
* For example:
|
||||
*
|
||||
* Child table with string and integer:
|
||||
* struct acpi_dp *child = acpi_dp_new_table("CHLD");
|
||||
* acpi_dp_add_string(child, "childstring", "CHILD");
|
||||
* acpi_dp_add_integer(child, "childint", 100);
|
||||
*
|
||||
* _DSD table with integer and gpio and child pointer:
|
||||
* struct acpi_dp *dsd = acpi_dp_new_table("_DSD");
|
||||
* acpi_dp_add_integer(dsd, "number1", 1);
|
||||
* acpi_dp_add_gpio(dsd, "gpio", "\_SB.PCI0.GPIO", 0, 0, 1);
|
||||
* acpi_dp_add_child(dsd, "child", child);
|
||||
*
|
||||
* Write entries into SSDT and clean up resources:
|
||||
* acpi_dp_write(dsd);
|
||||
*
|
||||
* Name(_DSD, Package() {
|
||||
* ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301")
|
||||
* Package() {
|
||||
* Package() { "gpio", Package() { \_SB.PCI0.GPIO, 0, 0, 0 } }
|
||||
* Package() { "number1", 1 }
|
||||
* }
|
||||
* ToUUID("dbb8e3e6-5886-4ba6-8795-1319f52a966b")
|
||||
* Package() {
|
||||
* Package() { "child", CHLD }
|
||||
* }
|
||||
* }
|
||||
* Name(CHLD, Package() {
|
||||
* ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301")
|
||||
* Package() {
|
||||
* Package() { "childstring", "CHILD" }
|
||||
* Package() { "childint", 100 }
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
|
||||
#define ACPI_DP_UUID "daffd814-6eba-4d8c-8a91-bc9bbf4aa301"
|
||||
#define ACPI_DP_CHILD_UUID "dbb8e3e6-5886-4ba6-8795-1319f52a966b"
|
||||
|
||||
/**
|
||||
* enum acpi_dp_type - types of device property objects
|
||||
*
|
||||
* These refer to the types defined by struct acpi_dp below
|
||||
*
|
||||
* @ACPI_DP_TYPE_UNKNOWN: Unknown / do not use
|
||||
* @ACPI_DP_TYPE_INTEGER: Integer value (u64) in @integer
|
||||
* @ACPI_DP_TYPE_STRING: String value in @string
|
||||
* @ACPI_DP_TYPE_REFERENCE: Reference to another object, with value in @string
|
||||
* @ACPI_DP_TYPE_TABLE: Type for a top-level table which may have children
|
||||
* @ACPI_DP_TYPE_ARRAY: Array of items with first item in @array and following
|
||||
* items linked from that item's @next
|
||||
* @ACPI_DP_TYPE_CHILD: Child object, with siblings in that child's @next
|
||||
*/
|
||||
enum acpi_dp_type {
|
||||
ACPI_DP_TYPE_UNKNOWN,
|
||||
ACPI_DP_TYPE_INTEGER,
|
||||
ACPI_DP_TYPE_STRING,
|
||||
ACPI_DP_TYPE_REFERENCE,
|
||||
ACPI_DP_TYPE_TABLE,
|
||||
ACPI_DP_TYPE_ARRAY,
|
||||
ACPI_DP_TYPE_CHILD,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct acpi_dp - ACPI device properties
|
||||
*
|
||||
* @type: Table type
|
||||
* @name: Name of object, typically _DSD but could be CHLD for a child object.
|
||||
* This can be NULL if there is no name
|
||||
* @next: Next object in list (next array element or next sibling)
|
||||
* @child: Pointer to first child, if @type == ACPI_DP_TYPE_CHILD, else NULL
|
||||
* @array: First array element, if @type == ACPI_DP_TYPE_ARRAY, else NULL
|
||||
* @integer: Integer value of the property, if @type == ACPI_DP_TYPE_INTEGER
|
||||
* @string: String value of the property, if @type == ACPI_DP_TYPE_STRING;
|
||||
* child name if @type == ACPI_DP_TYPE_CHILD;
|
||||
* reference name if @type == ACPI_DP_TYPE_REFERENCE;
|
||||
*/
|
||||
struct acpi_dp {
|
||||
enum acpi_dp_type type;
|
||||
const char *name;
|
||||
struct acpi_dp *next;
|
||||
union {
|
||||
struct acpi_dp *child;
|
||||
struct acpi_dp *array;
|
||||
};
|
||||
union {
|
||||
u64 integer;
|
||||
const char *string;
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* acpi_dp_new_table() - Start a new Device Property table
|
||||
*
|
||||
* @ref: ACPI reference (e.g. "_DSD")
|
||||
* @return pointer to table, or NULL if out of memory
|
||||
*/
|
||||
struct acpi_dp *acpi_dp_new_table(const char *ref);
|
||||
|
||||
/**
|
||||
* acpi_dp_add_integer() - Add integer Device Property
|
||||
*
|
||||
* A new node is added to the end of the property list of @dp
|
||||
*
|
||||
* @dp: Table to add this property to
|
||||
* @name: Name of property, or NULL for none
|
||||
* @value: Integer value
|
||||
* @return pointer to new node, or NULL if out of memory
|
||||
*/
|
||||
struct acpi_dp *acpi_dp_add_integer(struct acpi_dp *dp, const char *name,
|
||||
u64 value);
|
||||
|
||||
/**
|
||||
* acpi_dp_add_string() - Add string Device Property
|
||||
*
|
||||
* A new node is added to the end of the property list of @dp
|
||||
*
|
||||
* @dp: Table to add this property to
|
||||
* @name: Name of property, or NULL for none
|
||||
* @string: String value
|
||||
* @return pointer to new node, or NULL if out of memory
|
||||
*/
|
||||
struct acpi_dp *acpi_dp_add_string(struct acpi_dp *dp, const char *name,
|
||||
const char *string);
|
||||
|
||||
/**
|
||||
* acpi_dp_add_reference() - Add reference Device Property
|
||||
*
|
||||
* A new node is added to the end of the property list of @dp
|
||||
*
|
||||
* @dp: Table to add this property to
|
||||
* @name: Name of property, or NULL for none
|
||||
* @reference: Reference value
|
||||
* @return pointer to new node, or NULL if out of memory
|
||||
*/
|
||||
struct acpi_dp *acpi_dp_add_reference(struct acpi_dp *dp, const char *name,
|
||||
const char *reference);
|
||||
|
||||
/**
|
||||
* acpi_dp_add_array() - Add array Device Property
|
||||
*
|
||||
* A new node is added to the end of the property list of @dp, with the array
|
||||
* attached to that.
|
||||
*
|
||||
* @dp: Table to add this property to
|
||||
* @name: Name of property, or NULL for none
|
||||
* @return pointer to new node, or NULL if out of memory
|
||||
*/
|
||||
struct acpi_dp *acpi_dp_add_array(struct acpi_dp *dp, struct acpi_dp *array);
|
||||
|
||||
/**
|
||||
* acpi_dp_add_integer_array() - Add an array of integers
|
||||
*
|
||||
* A new node is added to the end of the property list of @dp, with the array
|
||||
* attached to that. Each element of the array becomes a new node.
|
||||
*
|
||||
* @dp: Table to add this property to
|
||||
* @name: Name of property, or NULL for none
|
||||
* @return pointer to new array node, or NULL if out of memory
|
||||
*/
|
||||
struct acpi_dp *acpi_dp_add_integer_array(struct acpi_dp *dp, const char *name,
|
||||
u64 *array, int len);
|
||||
|
||||
/**
|
||||
* acpi_dp_add_child() - Add a child table of Device Properties
|
||||
*
|
||||
* A new node is added as a child of @dp
|
||||
*
|
||||
* @dp: Table to add this child to
|
||||
* @name: Name of child, or NULL for none
|
||||
* @child: Child node to add
|
||||
* @return pointer to new child node, or NULL if out of memory
|
||||
*/
|
||||
struct acpi_dp *acpi_dp_add_child(struct acpi_dp *dp, const char *name,
|
||||
struct acpi_dp *child);
|
||||
|
||||
/**
|
||||
* acpi_dp_add_gpio() - Add a GPIO to a list of Device Properties
|
||||
*
|
||||
* A new node is added to the end of the property list of @dp, with the
|
||||
* GPIO properties added to the the new node
|
||||
*
|
||||
* @dp: Table to add this property to
|
||||
* @name: Name of property
|
||||
* @ref: Reference to device with a _CRS containing GpioIO or GpioInt
|
||||
* @index: Index of the GPIO resource in _CRS starting from zero
|
||||
* @pin: Pin in the GPIO resource, typically zero
|
||||
* @polarity: GPIO polarity. Note that ACPI_IRQ_ACTIVE_BOTH is not supported
|
||||
* @return pointer to new node, or NULL if out of memory
|
||||
*/
|
||||
struct acpi_dp *acpi_dp_add_gpio(struct acpi_dp *dp, const char *name,
|
||||
const char *ref, int index, int pin,
|
||||
enum acpi_irq_polarity polarity);
|
||||
|
||||
/**
|
||||
* acpi_dp_write() - Write Device Property hierarchy and clean up resources
|
||||
*
|
||||
* This writes the table using acpigen and then frees it
|
||||
*
|
||||
* @ctx: ACPI context
|
||||
* @table: Table to write
|
||||
* @return 0 if OK, -ve on error
|
||||
*/
|
||||
int acpi_dp_write(struct acpi_ctx *ctx, struct acpi_dp *table);
|
||||
|
||||
/**
|
||||
* acpi_dp_ofnode_copy_int() - Copy a property from device tree to DP
|
||||
*
|
||||
* This copies an integer property from the device tree to the ACPI DP table.
|
||||
*
|
||||
* @node: Node to copy from
|
||||
* @dp: DP to copy to
|
||||
* @prop: Property name to copy
|
||||
* @return 0 if OK, -ve on error
|
||||
*/
|
||||
int acpi_dp_ofnode_copy_int(ofnode node, struct acpi_dp *dp, const char *prop);
|
||||
|
||||
/**
|
||||
* acpi_dp_ofnode_copy_str() - Copy a property from device tree to DP
|
||||
*
|
||||
* This copies a string property from the device tree to the ACPI DP table.
|
||||
*
|
||||
* @node: Node to copy from
|
||||
* @dp: DP to copy to
|
||||
* @prop: Property name to copy
|
||||
* @return 0 if OK, -ve on error
|
||||
*/
|
||||
int acpi_dp_ofnode_copy_str(ofnode node, struct acpi_dp *dp, const char *prop);
|
||||
|
||||
/**
|
||||
* acpi_dp_dev_copy_int() - Copy a property from device tree to DP
|
||||
*
|
||||
* This copies an integer property from the device tree to the ACPI DP table.
|
||||
*
|
||||
* @dev: Device to copy from
|
||||
* @dp: DP to copy to
|
||||
* @prop: Property name to copy
|
||||
* @return 0 if OK, -ve on error
|
||||
*/
|
||||
int acpi_dp_dev_copy_int(const struct udevice *dev, struct acpi_dp *dp,
|
||||
const char *prop);
|
||||
|
||||
/**
|
||||
* acpi_dp_dev_copy_str() - Copy a property from device tree to DP
|
||||
*
|
||||
* This copies a string property from the device tree to the ACPI DP table.
|
||||
*
|
||||
* @dev: Device to copy from
|
||||
* @dp: DP to copy to
|
||||
* @prop: Property name to copy
|
||||
* @return 0 if OK, -ve on error
|
||||
*/
|
||||
int acpi_dp_dev_copy_str(const struct udevice *dev, struct acpi_dp *dp,
|
||||
const char *prop);
|
||||
|
||||
#endif
|
566
include/acpi/acpigen.h
Normal file
566
include/acpi/acpigen.h
Normal file
|
@ -0,0 +1,566 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Core ACPI (Advanced Configuration and Power Interface) support
|
||||
*
|
||||
* Copyright 2019 Google LLC
|
||||
*
|
||||
* Modified from coreboot file acpigen.h
|
||||
*/
|
||||
|
||||
#ifndef __ACPI_ACPIGEN_H
|
||||
#define __ACPI_ACPIGEN_H
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
struct acpi_ctx;
|
||||
struct acpi_gen_regaddr;
|
||||
struct acpi_gpio;
|
||||
|
||||
/* Top 4 bits of the value used to indicate a three-byte length value */
|
||||
#define ACPI_PKG_LEN_3_BYTES 0x80
|
||||
|
||||
#define ACPI_METHOD_NARGS_MASK 0x7
|
||||
#define ACPI_METHOD_SERIALIZED_MASK BIT(3)
|
||||
|
||||
#define ACPI_END_TAG 0x79
|
||||
|
||||
/* ACPI Op/Prefix codes */
|
||||
enum {
|
||||
ZERO_OP = 0x00,
|
||||
ONE_OP = 0x01,
|
||||
NAME_OP = 0x08,
|
||||
BYTE_PREFIX = 0x0a,
|
||||
WORD_PREFIX = 0x0b,
|
||||
DWORD_PREFIX = 0x0c,
|
||||
STRING_PREFIX = 0x0d,
|
||||
QWORD_PREFIX = 0x0e,
|
||||
SCOPE_OP = 0x10,
|
||||
BUFFER_OP = 0x11,
|
||||
PACKAGE_OP = 0x12,
|
||||
METHOD_OP = 0x14,
|
||||
SLEEP_OP = 0x22,
|
||||
DUAL_NAME_PREFIX = 0x2e,
|
||||
MULTI_NAME_PREFIX = 0x2f,
|
||||
DEBUG_OP = 0x31,
|
||||
EXT_OP_PREFIX = 0x5b,
|
||||
ROOT_PREFIX = 0x5c,
|
||||
LOCAL0_OP = 0x60,
|
||||
LOCAL1_OP = 0x61,
|
||||
LOCAL2_OP = 0x62,
|
||||
LOCAL3_OP = 0x63,
|
||||
LOCAL4_OP = 0x64,
|
||||
LOCAL5_OP = 0x65,
|
||||
LOCAL6_OP = 0x66,
|
||||
LOCAL7_OP = 0x67,
|
||||
STORE_OP = 0x70,
|
||||
AND_OP = 0x7b,
|
||||
OR_OP = 0x7d,
|
||||
NOT_OP = 0x80,
|
||||
DEVICE_OP = 0x82,
|
||||
POWER_RES_OP = 0x84,
|
||||
RETURN_OP = 0xa4,
|
||||
};
|
||||
|
||||
/**
|
||||
* acpigen_get_current() - Get the current ACPI code output pointer
|
||||
*
|
||||
* @ctx: ACPI context pointer
|
||||
* @return output pointer
|
||||
*/
|
||||
u8 *acpigen_get_current(struct acpi_ctx *ctx);
|
||||
|
||||
/**
|
||||
* acpigen_emit_byte() - Emit a byte to the ACPI code
|
||||
*
|
||||
* @ctx: ACPI context pointer
|
||||
* @data: Value to output
|
||||
*/
|
||||
void acpigen_emit_byte(struct acpi_ctx *ctx, uint data);
|
||||
|
||||
/**
|
||||
* acpigen_emit_word() - Emit a 16-bit word to the ACPI code
|
||||
*
|
||||
* @ctx: ACPI context pointer
|
||||
* @data: Value to output
|
||||
*/
|
||||
void acpigen_emit_word(struct acpi_ctx *ctx, uint data);
|
||||
|
||||
/**
|
||||
* acpigen_emit_dword() - Emit a 32-bit 'double word' to the ACPI code
|
||||
*
|
||||
* @ctx: ACPI context pointer
|
||||
* @data: Value to output
|
||||
*/
|
||||
void acpigen_emit_dword(struct acpi_ctx *ctx, uint data);
|
||||
|
||||
/**
|
||||
* acpigen_emit_stream() - Emit a stream of bytes
|
||||
*
|
||||
* @ctx: ACPI context pointer
|
||||
* @data: Data to output
|
||||
* @size: Size of data in bytes
|
||||
*/
|
||||
void acpigen_emit_stream(struct acpi_ctx *ctx, const char *data, int size);
|
||||
|
||||
/**
|
||||
* acpigen_emit_string() - Emit a string
|
||||
*
|
||||
* Emit a string with a null terminator
|
||||
*
|
||||
* @ctx: ACPI context pointer
|
||||
* @str: String to output, or NULL for an empty string
|
||||
*/
|
||||
void acpigen_emit_string(struct acpi_ctx *ctx, const char *str);
|
||||
|
||||
/**
|
||||
* acpigen_write_len_f() - Write a 'forward' length placeholder
|
||||
*
|
||||
* This adds space for a length value in the ACPI stream and pushes the current
|
||||
* position (before the length) on the stack. After calling this you can write
|
||||
* some data and then call acpigen_pop_len() to update the length value.
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* acpigen_write_len_f() ------\
|
||||
* acpigen_write...() |
|
||||
* acpigen_write...() |
|
||||
* acpigen_write_len_f() --\ |
|
||||
* acpigen_write...() | |
|
||||
* acpigen_write...() | |
|
||||
* acpigen_pop_len() ------/ |
|
||||
* acpigen_write...() |
|
||||
* acpigen_pop_len() ----------/
|
||||
*
|
||||
* See ACPI 6.3 section 20.2.4 Package Length Encoding
|
||||
*
|
||||
* This implementation always uses a 3-byte packet length for simplicity. It
|
||||
* could be adjusted to support other lengths.
|
||||
*
|
||||
* @ctx: ACPI context pointer
|
||||
*/
|
||||
void acpigen_write_len_f(struct acpi_ctx *ctx);
|
||||
|
||||
/**
|
||||
* acpigen_pop_len() - Update the previously stacked length placeholder
|
||||
*
|
||||
* Call this after the data for the block has been written. It updates the
|
||||
* top length value in the stack and pops it off.
|
||||
*
|
||||
* @ctx: ACPI context pointer
|
||||
*/
|
||||
void acpigen_pop_len(struct acpi_ctx *ctx);
|
||||
|
||||
/**
|
||||
* acpigen_write_package() - Start writing a package
|
||||
*
|
||||
* A package collects together a number of elements in the ACPI code. To write
|
||||
* a package use:
|
||||
*
|
||||
* acpigen_write_package(ctx, 3);
|
||||
* ...write things
|
||||
* acpigen_pop_len()
|
||||
*
|
||||
* If you don't know the number of elements in advance, acpigen_write_package()
|
||||
* returns a pointer to the value so you can update it later:
|
||||
*
|
||||
* char *num_elements = acpigen_write_package(ctx, 0);
|
||||
* ...write things
|
||||
* *num_elements += 1;
|
||||
* ...write things
|
||||
* *num_elements += 1;
|
||||
* acpigen_pop_len()
|
||||
*
|
||||
* @ctx: ACPI context pointer
|
||||
* @nr_el: Number of elements (0 if not known)
|
||||
* @returns pointer to the number of elements, which can be updated by the
|
||||
* caller if needed
|
||||
*/
|
||||
char *acpigen_write_package(struct acpi_ctx *ctx, int nr_el);
|
||||
|
||||
/**
|
||||
* acpigen_write_byte() - Write a byte
|
||||
*
|
||||
* @ctx: ACPI context pointer
|
||||
* @data: Value to write
|
||||
*/
|
||||
void acpigen_write_byte(struct acpi_ctx *ctx, unsigned int data);
|
||||
|
||||
/**
|
||||
* acpigen_write_word() - Write a word
|
||||
*
|
||||
* @ctx: ACPI context pointer
|
||||
* @data: Value to write
|
||||
*/
|
||||
void acpigen_write_word(struct acpi_ctx *ctx, unsigned int data);
|
||||
|
||||
/**
|
||||
* acpigen_write_dword() - Write a dword
|
||||
*
|
||||
* @ctx: ACPI context pointer
|
||||
* @data: Value to write
|
||||
*/
|
||||
void acpigen_write_dword(struct acpi_ctx *ctx, unsigned int data);
|
||||
|
||||
/**
|
||||
* acpigen_write_qword() - Write a qword
|
||||
*
|
||||
* @ctx: ACPI context pointer
|
||||
* @data: Value to write
|
||||
*/
|
||||
void acpigen_write_qword(struct acpi_ctx *ctx, u64 data);
|
||||
|
||||
/**
|
||||
* acpigen_write_zero() - Write zero
|
||||
*
|
||||
* @ctx: ACPI context pointer
|
||||
*/
|
||||
void acpigen_write_zero(struct acpi_ctx *ctx);
|
||||
|
||||
/**
|
||||
* acpigen_write_one() - Write one
|
||||
*
|
||||
* @ctx: ACPI context pointer
|
||||
*/
|
||||
void acpigen_write_one(struct acpi_ctx *ctx);
|
||||
|
||||
/**
|
||||
* acpigen_write_integer() - Write an integer
|
||||
*
|
||||
* This writes an operation (BYTE_OP, WORD_OP, DWORD_OP, QWORD_OP depending on
|
||||
* the integer size) and an integer value. Note that WORD means 16 bits in ACPI.
|
||||
*
|
||||
* @ctx: ACPI context pointer
|
||||
* @data: Integer to write
|
||||
*/
|
||||
void acpigen_write_integer(struct acpi_ctx *ctx, u64 data);
|
||||
|
||||
/**
|
||||
* acpigen_write_name_zero() - Write a named zero value
|
||||
*
|
||||
* @ctx: ACPI context pointer
|
||||
* @name: Name of the value
|
||||
*/
|
||||
void acpigen_write_name_zero(struct acpi_ctx *ctx, const char *name);
|
||||
|
||||
/**
|
||||
* acpigen_write_name_one() - Write a named one value
|
||||
*
|
||||
* @ctx: ACPI context pointer
|
||||
* @name: Name of the value
|
||||
*/
|
||||
void acpigen_write_name_one(struct acpi_ctx *ctx, const char *name);
|
||||
|
||||
/**
|
||||
* acpigen_write_name_byte() - Write a named byte value
|
||||
*
|
||||
* @ctx: ACPI context pointer
|
||||
* @name: Name of the value
|
||||
* @val: Value to write
|
||||
*/
|
||||
void acpigen_write_name_byte(struct acpi_ctx *ctx, const char *name, uint val);
|
||||
|
||||
/**
|
||||
* acpigen_write_name_word() - Write a named word value
|
||||
*
|
||||
* @ctx: ACPI context pointer
|
||||
* @name: Name of the value
|
||||
* @val: Value to write
|
||||
*/
|
||||
void acpigen_write_name_word(struct acpi_ctx *ctx, const char *name, uint val);
|
||||
|
||||
/**
|
||||
* acpigen_write_name_dword() - Write a named dword value
|
||||
*
|
||||
* @ctx: ACPI context pointer
|
||||
* @name: Name of the value
|
||||
* @val: Value to write
|
||||
*/
|
||||
void acpigen_write_name_dword(struct acpi_ctx *ctx, const char *name, uint val);
|
||||
|
||||
/**
|
||||
* acpigen_write_name_qword() - Write a named qword value
|
||||
*
|
||||
* @ctx: ACPI context pointer
|
||||
* @name: Name of the value
|
||||
* @val: Value to write
|
||||
*/
|
||||
void acpigen_write_name_qword(struct acpi_ctx *ctx, const char *name, u64 val);
|
||||
|
||||
/**
|
||||
* acpigen_write_name_integer() - Write a named integer value
|
||||
*
|
||||
* @ctx: ACPI context pointer
|
||||
* @name: Name of the value
|
||||
* @val: Value to write
|
||||
*/
|
||||
void acpigen_write_name_integer(struct acpi_ctx *ctx, const char *name,
|
||||
u64 val);
|
||||
|
||||
/**
|
||||
* acpigen_write_name_string() - Write a named string value
|
||||
*
|
||||
* @ctx: ACPI context pointer
|
||||
* @name: Name of the value
|
||||
* @string: String to write
|
||||
*/
|
||||
void acpigen_write_name_string(struct acpi_ctx *ctx, const char *name,
|
||||
const char *string);
|
||||
|
||||
/**
|
||||
* acpigen_write_string() - Write a string
|
||||
*
|
||||
* This writes a STRING_PREFIX followed by a null-terminated string
|
||||
*
|
||||
* @ctx: ACPI context pointer
|
||||
* @str: String to write
|
||||
*/
|
||||
void acpigen_write_string(struct acpi_ctx *ctx, const char *str);
|
||||
|
||||
/**
|
||||
* acpigen_emit_namestring() - Emit an ACPI name
|
||||
*
|
||||
* This writes out an ACPI name or path in the required special format. It does
|
||||
* not add the NAME_OP prefix.
|
||||
*
|
||||
* @ctx: ACPI context pointer
|
||||
* @namepath: Name / path to emit
|
||||
*/
|
||||
void acpigen_emit_namestring(struct acpi_ctx *ctx, const char *namepath);
|
||||
|
||||
/**
|
||||
* acpigen_write_name() - Write out an ACPI name
|
||||
*
|
||||
* This writes out an ACPI name or path in the required special format with a
|
||||
* NAME_OP prefix.
|
||||
*
|
||||
* @ctx: ACPI context pointer
|
||||
* @namepath: Name / path to emit
|
||||
*/
|
||||
void acpigen_write_name(struct acpi_ctx *ctx, const char *namepath);
|
||||
|
||||
/**
|
||||
* acpigen_write_scope() - Write a scope
|
||||
*
|
||||
* @ctx: ACPI context pointer
|
||||
* @scope: Scope to write (e.g. "\\_SB.ABCD")
|
||||
*/
|
||||
void acpigen_write_scope(struct acpi_ctx *ctx, const char *scope);
|
||||
|
||||
/**
|
||||
* acpigen_write_uuid() - Write a UUID
|
||||
*
|
||||
* This writes out a UUID in the format used by ACPI, with a BUFFER_OP prefix.
|
||||
*
|
||||
* @ctx: ACPI context pointer
|
||||
* @uuid: UUID to write in the form aabbccdd-eeff-gghh-iijj-kkllmmnnoopp
|
||||
* @return 0 if OK, -EINVAL if the format is incorrect
|
||||
*/
|
||||
int acpigen_write_uuid(struct acpi_ctx *ctx, const char *uuid);
|
||||
|
||||
/**
|
||||
* acpigen_emit_ext_op() - Emit an extended op with the EXT_OP_PREFIX prefix
|
||||
*
|
||||
* @ctx: ACPI context pointer
|
||||
* @op: Operation code (e.g. SLEEP_OP)
|
||||
*/
|
||||
void acpigen_emit_ext_op(struct acpi_ctx *ctx, uint op);
|
||||
|
||||
/**
|
||||
* acpigen_write_method() - Write a method header
|
||||
*
|
||||
* @ctx: ACPI context pointer
|
||||
* @name: Method name (4 characters)
|
||||
* @nargs: Number of method arguments (0 if none)
|
||||
*/
|
||||
void acpigen_write_method(struct acpi_ctx *ctx, const char *name, int nargs);
|
||||
|
||||
/**
|
||||
* acpigen_write_method_serialized() - Write a method header
|
||||
*
|
||||
* This sets the 'serialized' flag so that the method is thread-safe
|
||||
*
|
||||
* @ctx: ACPI context pointer
|
||||
* @name: Method name (4 characters)
|
||||
* @nargs: Number of method arguments (0 if none)
|
||||
*/
|
||||
void acpigen_write_method_serialized(struct acpi_ctx *ctx, const char *name,
|
||||
int nargs);
|
||||
|
||||
/**
|
||||
* acpigen_write_device() - Write an ACPI device
|
||||
*
|
||||
* @ctx: ACPI context pointer
|
||||
* @name: Device name to write
|
||||
*/
|
||||
void acpigen_write_device(struct acpi_ctx *ctx, const char *name);
|
||||
|
||||
/**
|
||||
* acpigen_write_sta() - Write a _STA method
|
||||
*
|
||||
* @ctx: ACPI context pointer
|
||||
* @status: Status value to return
|
||||
*/
|
||||
void acpigen_write_sta(struct acpi_ctx *ctx, uint status);
|
||||
|
||||
/**
|
||||
* acpigen_write_resourcetemplate_header() - Write a ResourceTemplate header
|
||||
*
|
||||
* @ctx: ACPI context pointer
|
||||
*/
|
||||
void acpigen_write_resourcetemplate_header(struct acpi_ctx *ctx);
|
||||
|
||||
/**
|
||||
* acpigen_write_resourcetemplate_footer() - Write a ResourceTemplate footer
|
||||
*
|
||||
* @ctx: ACPI context pointer
|
||||
*/
|
||||
void acpigen_write_resourcetemplate_footer(struct acpi_ctx *ctx);
|
||||
|
||||
/**
|
||||
* acpigen_write_register_resource() - Write a register resource
|
||||
*
|
||||
* This writes a header, the address information and a footer
|
||||
*
|
||||
* @ctx: ACPI context pointer
|
||||
* @addr: Address to write
|
||||
*/
|
||||
void acpigen_write_register_resource(struct acpi_ctx *ctx,
|
||||
const struct acpi_gen_regaddr *addr);
|
||||
|
||||
/**
|
||||
* acpigen_write_sleep() - Write a sleep operation
|
||||
*
|
||||
* @ctx: ACPI context pointer
|
||||
* @sleep_ms: Number of milliseconds to sleep for
|
||||
*/
|
||||
void acpigen_write_sleep(struct acpi_ctx *ctx, u64 sleep_ms);
|
||||
|
||||
/**
|
||||
* acpigen_write_store() - Write a store operation
|
||||
*
|
||||
* @ctx: ACPI context pointer
|
||||
*/
|
||||
void acpigen_write_store(struct acpi_ctx *ctx);
|
||||
|
||||
/**
|
||||
* acpigen_write_debug_string() - Write a debug string
|
||||
*
|
||||
* This writes a debug operation with an associated string
|
||||
*
|
||||
* @ctx: ACPI context pointer
|
||||
* @str: String to write
|
||||
*/
|
||||
void acpigen_write_debug_string(struct acpi_ctx *ctx, const char *str);
|
||||
|
||||
/**
|
||||
* acpigen_write_or() - Write a bitwise OR operation
|
||||
*
|
||||
* res = arg1 | arg2
|
||||
*
|
||||
* @ctx: ACPI context pointer
|
||||
* @arg1: ACPI opcode for operand 1 (e.g. LOCAL0_OP)
|
||||
* @arg2: ACPI opcode for operand 2 (e.g. LOCAL1_OP)
|
||||
* @res: ACPI opcode for result (e.g. LOCAL2_OP)
|
||||
*/
|
||||
void acpigen_write_or(struct acpi_ctx *ctx, u8 arg1, u8 arg2, u8 res);
|
||||
|
||||
/**
|
||||
* acpigen_write_and() - Write a bitwise AND operation
|
||||
*
|
||||
* res = arg1 & arg2
|
||||
*
|
||||
* @ctx: ACPI context pointer
|
||||
* @arg1: ACPI opcode for operand 1 (e.g. LOCAL0_OP)
|
||||
* @arg2: ACPI opcode for operand 2 (e.g. LOCAL1_OP)
|
||||
* @res: ACPI opcode for result (e.g. LOCAL2_OP)
|
||||
*/
|
||||
void acpigen_write_and(struct acpi_ctx *ctx, u8 arg1, u8 arg2, u8 res);
|
||||
|
||||
/**
|
||||
* acpigen_write_not() - Write a bitwise NOT operation
|
||||
*
|
||||
* res = ~arg1
|
||||
*
|
||||
* @ctx: ACPI context pointer
|
||||
* @arg: ACPI opcode for operand (e.g. LOCAL0_OP)
|
||||
* @res: ACPI opcode for result (e.g. LOCAL2_OP)
|
||||
*/
|
||||
void acpigen_write_not(struct acpi_ctx *ctx, u8 arg, u8 res);
|
||||
|
||||
/**
|
||||
* acpigen_write_power_res() - Write a power resource
|
||||
*
|
||||
* Name (_PRx, Package(One) { name })
|
||||
* ...
|
||||
* PowerResource (name, level, order)
|
||||
*
|
||||
* The caller should fill in the rest of the power resource and then call
|
||||
* acpigen_pop_len() to close it off
|
||||
*
|
||||
* @ctx: ACPI context pointer
|
||||
* @name: Name of power resource (e.g. "PRIC")
|
||||
* @level: Deepest sleep level that this resource must be kept on (0=S0, 3=S3)
|
||||
* @order: Order that this must be enabled/disabled (e.g. 0)
|
||||
* @dev_stats: List of states to define, e.g. {"_PR0", "_PR3"}
|
||||
* @dev_states_count: Number of dev states
|
||||
*/
|
||||
void acpigen_write_power_res(struct acpi_ctx *ctx, const char *name, uint level,
|
||||
uint order, const char *const dev_states[],
|
||||
size_t dev_states_count);
|
||||
|
||||
/**
|
||||
* acpigen_set_enable_tx_gpio() - Emit ACPI code to enable/disable a GPIO
|
||||
*
|
||||
* This emits code to either enable to disable a Tx GPIO. It takes account of
|
||||
* the GPIO polarity.
|
||||
*
|
||||
* The code needs access to the DW0 register for the pad being used. This is
|
||||
* provided by gpio->pin0_addr and ACPI methods must be defined for the board
|
||||
* which can read and write the pad's DW0 register given this address:
|
||||
* @dw0_read: takes a single argument, the DW0 address
|
||||
* returns the DW0 value
|
||||
* @dw0:write: takes two arguments, the DW0 address and the value to write
|
||||
* no return value
|
||||
*
|
||||
* Example code (-- means comment):
|
||||
*
|
||||
* -- Get Pad Configuration DW0 register value
|
||||
* Method (GPC0, 0x1, Serialized)
|
||||
* {
|
||||
* -- Arg0 - GPIO DW0 address
|
||||
* Store (Arg0, Local0)
|
||||
* OperationRegion (PDW0, SystemMemory, Local0, 4)
|
||||
* Field (PDW0, AnyAcc, NoLock, Preserve) {
|
||||
* TEMP, 32
|
||||
* }
|
||||
* Return (TEMP)
|
||||
* }
|
||||
*
|
||||
* -- Set Pad Configuration DW0 register value
|
||||
* Method (SPC0, 0x2, Serialized)
|
||||
* {
|
||||
* -- Arg0 - GPIO DW0 address
|
||||
* -- Arg1 - Value for DW0 register
|
||||
* Store (Arg0, Local0)
|
||||
* OperationRegion (PDW0, SystemMemory, Local0, 4)
|
||||
* Field (PDW0, AnyAcc, NoLock, Preserve) {
|
||||
* TEMP,32
|
||||
* }
|
||||
* Store (Arg1, TEMP)
|
||||
* }
|
||||
*
|
||||
*
|
||||
* @ctx: ACPI context pointer
|
||||
* @tx_state_val: Mask to use to toggle the TX state on the GPIO pin, e,g.
|
||||
* PAD_CFG0_TX_STATE
|
||||
* @dw0_read: Method name to use to read dw0, e.g. "\\_SB.GPC0"
|
||||
* @dw0_write: Method name to use to read dw0, e.g. "\\_SB.SPC0"
|
||||
* @gpio: GPIO to change
|
||||
* @enable: true to enable GPIO, false to disable
|
||||
* Returns 0 on success, -ve on error.
|
||||
*/
|
||||
int acpigen_set_enable_tx_gpio(struct acpi_ctx *ctx, u32 tx_state_val,
|
||||
const char *dw0_read, const char *dw0_write,
|
||||
struct acpi_gpio *gpio, bool enable);
|
||||
|
||||
#endif
|
|
@ -10,6 +10,7 @@
|
|||
#include <dm/ofnode.h>
|
||||
#include <linux/bitops.h>
|
||||
|
||||
struct acpi_gpio;
|
||||
struct ofnode_phandle_args;
|
||||
|
||||
/*
|
||||
|
@ -329,6 +330,20 @@ struct dm_gpio_ops {
|
|||
*/
|
||||
int (*get_dir_flags)(struct udevice *dev, unsigned int offset,
|
||||
ulong *flags);
|
||||
|
||||
#if CONFIG_IS_ENABLED(ACPIGEN)
|
||||
/**
|
||||
* get_acpi() - Get the ACPI info for a GPIO
|
||||
*
|
||||
* This converts a GPIO to an ACPI structure for adding to the ACPI
|
||||
* tables.
|
||||
*
|
||||
* @desc: GPIO description to convert
|
||||
* @gpio: Output ACPI GPIO information
|
||||
* @return ACPI pin number or -ve on error
|
||||
*/
|
||||
int (*get_acpi)(const struct gpio_desc *desc, struct acpi_gpio *gpio);
|
||||
#endif
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -674,4 +689,16 @@ int dm_gpio_get_dir_flags(struct gpio_desc *desc, ulong *flags);
|
|||
*/
|
||||
int gpio_get_number(const struct gpio_desc *desc);
|
||||
|
||||
/**
|
||||
* gpio_get_acpi() - Get the ACPI pin for a GPIO
|
||||
*
|
||||
* This converts a GPIO to an ACPI pin number for adding to the ACPI
|
||||
* tables. If the GPIO is invalid, the pin_count and pins[0] are set to 0
|
||||
*
|
||||
* @desc: GPIO description to convert
|
||||
* @gpio: Output ACPI GPIO information
|
||||
* @return ACPI pin number or -ve on error
|
||||
*/
|
||||
int gpio_get_acpi(const struct gpio_desc *desc, struct acpi_gpio *gpio);
|
||||
|
||||
#endif /* _ASM_GENERIC_GPIO_H_ */
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
#ifndef _BINMAN_H_
|
||||
#define _BINMAN_H_
|
||||
|
||||
#include <dm/ofnode.h>
|
||||
|
||||
/**
|
||||
*struct binman_entry - information about a binman entry
|
||||
*
|
||||
|
@ -20,6 +22,26 @@ struct binman_entry {
|
|||
u32 size;
|
||||
};
|
||||
|
||||
/**
|
||||
* binman_entry_map() - Look up the address of an entry in memory
|
||||
*
|
||||
* @parent: Parent binman node
|
||||
* @name: Name of entry
|
||||
* @bufp: Returns a pointer to the entry
|
||||
* @sizep: Returns the size of the entry
|
||||
* @return 0 on success, -EPERM if the ROM offset is not set, -ENOENT if the
|
||||
* entry cannot be found, other error code other error
|
||||
*/
|
||||
int binman_entry_map(ofnode parent, const char *name, void **bufp, int *sizep);
|
||||
|
||||
/**
|
||||
* binman_set_rom_offset() - Set the ROM memory-map offset
|
||||
*
|
||||
* @rom_offset: Offset from an image_pos to the memory-mapped address. This
|
||||
* tells binman that ROM image_pos x can be addressed at rom_offset + x
|
||||
*/
|
||||
void binman_set_rom_offset(int rom_offset);
|
||||
|
||||
/**
|
||||
* binman_entry_find() - Find a binman symbol
|
||||
*
|
||||
|
@ -33,6 +55,14 @@ struct binman_entry {
|
|||
*/
|
||||
int binman_entry_find(const char *name, struct binman_entry *entry);
|
||||
|
||||
/**
|
||||
* binman_section_find_node() - Find a binman node
|
||||
*
|
||||
* @name: Name of node to look for
|
||||
* @return Node that was found, ofnode_null() if not found
|
||||
*/
|
||||
ofnode binman_section_find_node(const char *name);
|
||||
|
||||
/**
|
||||
* binman_init() - Set up the binman symbol information
|
||||
*
|
||||
|
|
|
@ -16,30 +16,51 @@
|
|||
#define ACPI_OPS_PTR(_ptr)
|
||||
#endif
|
||||
|
||||
/* Length of an ACPI name string, excluding nul terminator */
|
||||
/* Length of an ACPI name string, excluding null terminator */
|
||||
#define ACPI_NAME_LEN 4
|
||||
|
||||
/* Length of an ACPI name string including nul terminator */
|
||||
#define ACPI_NAME_MAX (ACPI_NAME_LEN + 1)
|
||||
|
||||
/* Number of nested objects supported */
|
||||
#define ACPIGEN_LENSTACK_SIZE 10
|
||||
|
||||
#if !defined(__ACPI__)
|
||||
|
||||
struct nhlt;
|
||||
|
||||
/** enum acpi_dump_option - selects what ACPI information to dump */
|
||||
enum acpi_dump_option {
|
||||
ACPI_DUMP_LIST, /* Just the list of items */
|
||||
ACPI_DUMP_CONTENTS, /* Include the binary contents also */
|
||||
};
|
||||
|
||||
/**
|
||||
* struct acpi_ctx - Context used for writing ACPI tables
|
||||
*
|
||||
* This contains a few useful pieces of information used when writing
|
||||
*
|
||||
* @base: Base address of ACPI tables
|
||||
* @current: Current address for writing
|
||||
* @rsdp: Pointer to the Root System Description Pointer, typically used when
|
||||
* adding a new table. The RSDP holds pointers to the RSDT and XSDT.
|
||||
* @rsdt: Pointer to the Root System Description Table
|
||||
* @xsdt: Pointer to the Extended System Description Table
|
||||
* @nhlt: Intel Non-High-Definition-Audio Link Table (NHLT) pointer, used to
|
||||
* build up information that audio codecs need to provide in the NHLT ACPI
|
||||
* table
|
||||
* @len_stack: Stack of 'length' words to fix up later
|
||||
* @ltop: Points to current top of stack (0 = empty)
|
||||
*/
|
||||
struct acpi_ctx {
|
||||
void *base;
|
||||
void *current;
|
||||
struct acpi_rsdp *rsdp;
|
||||
struct acpi_rsdt *rsdt;
|
||||
struct acpi_xsdt *xsdt;
|
||||
struct nhlt *nhlt;
|
||||
char *len_stack[ACPIGEN_LENSTACK_SIZE];
|
||||
int ltop;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -65,6 +86,48 @@ struct acpi_ops {
|
|||
* @return 0 if OK, -ve on error
|
||||
*/
|
||||
int (*write_tables)(const struct udevice *dev, struct acpi_ctx *ctx);
|
||||
|
||||
/**
|
||||
* fill_ssdt() - Generate SSDT code for a device
|
||||
*
|
||||
* This is called to create the SSDT code. The method should write out
|
||||
* whatever ACPI code is needed by this device. It will end up in the
|
||||
* SSDT table.
|
||||
*
|
||||
* Note that this is called 'fill' because the entire contents of the
|
||||
* SSDT is build by calling this method on all devices.
|
||||
*
|
||||
* @dev: Device to write
|
||||
* @ctx: ACPI context to use
|
||||
* @return 0 if OK, -ve on error
|
||||
*/
|
||||
int (*fill_ssdt)(const struct udevice *dev, struct acpi_ctx *ctx);
|
||||
|
||||
/**
|
||||
* inject_dsdt() - Generate DSDT code for a device
|
||||
*
|
||||
* This is called to create the DSDT code. The method should write out
|
||||
* whatever ACPI code is needed by this device. It will end up in the
|
||||
* DSDT table.
|
||||
*
|
||||
* Note that this is called 'inject' because the output of calling this
|
||||
* method on all devices is injected into the DSDT, the bulk of which
|
||||
* is written in .asl files for the board.
|
||||
*
|
||||
* @dev: Device to write
|
||||
* @ctx: ACPI context to use
|
||||
* @return 0 if OK, -ve on error
|
||||
*/
|
||||
int (*inject_dsdt)(const struct udevice *dev, struct acpi_ctx *ctx);
|
||||
|
||||
/**
|
||||
* setup_nhlt() - Set up audio information for this device
|
||||
*
|
||||
* The method can add information to ctx->nhlt if it likes
|
||||
*
|
||||
* @return 0 if OK, -ENODATA if nothing to add, -ve on error
|
||||
*/
|
||||
int (*setup_nhlt)(const struct udevice *dev, struct acpi_ctx *ctx);
|
||||
};
|
||||
|
||||
#define device_get_acpi_ops(dev) ((dev)->driver->acpi_ops)
|
||||
|
@ -109,6 +172,60 @@ int acpi_copy_name(char *out_name, const char *name);
|
|||
*/
|
||||
int acpi_write_dev_tables(struct acpi_ctx *ctx);
|
||||
|
||||
/**
|
||||
* acpi_fill_ssdt() - Generate ACPI tables for SSDT
|
||||
*
|
||||
* This is called to create the SSDT code for all devices.
|
||||
*
|
||||
* @ctx: ACPI context to use
|
||||
* @return 0 if OK, -ve on error
|
||||
*/
|
||||
int acpi_fill_ssdt(struct acpi_ctx *ctx);
|
||||
|
||||
/**
|
||||
* acpi_inject_dsdt() - Generate ACPI tables for DSDT
|
||||
*
|
||||
* This is called to create the DSDT code for all devices.
|
||||
*
|
||||
* @ctx: ACPI context to use
|
||||
* @return 0 if OK, -ve on error
|
||||
*/
|
||||
int acpi_inject_dsdt(struct acpi_ctx *ctx);
|
||||
|
||||
/**
|
||||
* acpi_setup_nhlt() - Set up audio information
|
||||
*
|
||||
* This is called to set up the nhlt information for all devices.
|
||||
*
|
||||
* @ctx: ACPI context to use
|
||||
* @nhlt: Pointer to nhlt information to add to
|
||||
* @return 0 if OK, -ve on error
|
||||
*/
|
||||
int acpi_setup_nhlt(struct acpi_ctx *ctx, struct nhlt *nhlt);
|
||||
|
||||
/**
|
||||
* acpi_dump_items() - Dump out the collected ACPI items
|
||||
*
|
||||
* This lists the ACPI DSDT and SSDT items generated by the various U-Boot
|
||||
* drivers.
|
||||
*
|
||||
* @option: Sets what should be dumpyed
|
||||
*/
|
||||
void acpi_dump_items(enum acpi_dump_option option);
|
||||
|
||||
/**
|
||||
* acpi_get_path() - Get the full ACPI path for a device
|
||||
*
|
||||
* This checks for any override in the device tree and calls acpi_device_path()
|
||||
* if not
|
||||
*
|
||||
* @dev: Device to check
|
||||
* @out_path: Buffer to place the path in (should be ACPI_PATH_MAX long)
|
||||
* @maxlen: Size of buffer (typically ACPI_PATH_MAX)
|
||||
* @return 0 if OK, -ve on error
|
||||
*/
|
||||
int acpi_get_path(const struct udevice *dev, char *out_path, int maxlen);
|
||||
|
||||
#endif /* __ACPI__ */
|
||||
|
||||
#endif
|
||||
|
|
|
@ -764,7 +764,7 @@ int dev_enable_by_path(const char *path);
|
|||
*/
|
||||
static inline bool device_is_on_pci_bus(const struct udevice *dev)
|
||||
{
|
||||
return device_get_uclass_id(dev->parent) == UCLASS_PCI;
|
||||
return dev->parent && device_get_uclass_id(dev->parent) == UCLASS_PCI;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
14
include/dt-bindings/interrupt-controller/x86-irq.h
Normal file
14
include/dt-bindings/interrupt-controller/x86-irq.h
Normal file
|
@ -0,0 +1,14 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright 2019 Google LLC
|
||||
*
|
||||
* This provides additional flags used by x86.
|
||||
*/
|
||||
|
||||
#ifndef _DT_BINDINGS_INTERRUPT_CONTROLLER_X86_IRQ_H
|
||||
#define _DT_BINDINGS_INTERRUPT_CONTROLLER_X86_IRQ_H
|
||||
|
||||
#define X86_IRQ_TYPE_SHARED (1 << 4)
|
||||
#define X86_IRQ_TYPE_WAKE (1 << 5)
|
||||
|
||||
#endif
|
24
include/dt-bindings/sound/nhlt.h
Normal file
24
include/dt-bindings/sound/nhlt.h
Normal file
|
@ -0,0 +1,24 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright 2019 Google LLC
|
||||
*/
|
||||
|
||||
#ifndef _DT_BINDINGS_SOUND_NHLT_H
|
||||
#define _DT_BINDINGS_SOUND_NHLT_H
|
||||
|
||||
/* See Table 2-1. NHLT Endpoint Descriptor in the NHLT Specification (0.8.1) */
|
||||
#define NHLT_VID 0x8086
|
||||
#define NHLT_DID_DMIC 0xae20
|
||||
#define NHLT_DID_BT 0xae30
|
||||
#define NHLT_DID_SSP 0xae34
|
||||
|
||||
/* Hardware links available to use for codecs */
|
||||
#define AUDIO_LINK_SSP0 0
|
||||
#define AUDIO_LINK_SSP1 1
|
||||
#define AUDIO_LINK_SSP2 2
|
||||
#define AUDIO_LINK_SSP3 3
|
||||
#define AUDIO_LINK_SSP4 4
|
||||
#define AUDIO_LINK_SSP5 5
|
||||
#define AUDIO_LINK_DMIC 6
|
||||
|
||||
#endif /* _DT_BINDINGS_SOUND_NHLT_H */
|
|
@ -8,6 +8,9 @@
|
|||
#ifndef __irq_H
|
||||
#define __irq_H
|
||||
|
||||
struct acpi_irq;
|
||||
struct ofnode_phandle_args;
|
||||
|
||||
/*
|
||||
* Interrupt controller types available. You can find a particular one with
|
||||
* irq_first_device_type()
|
||||
|
@ -24,10 +27,12 @@ enum irq_dev_t {
|
|||
*
|
||||
* @dev: IRQ device that handles this irq
|
||||
* @id: ID to identify this irq with the device
|
||||
* @flags: Flags associated with this interrupt (IRQ_TYPE_...)
|
||||
*/
|
||||
struct irq {
|
||||
struct udevice *dev;
|
||||
ulong id;
|
||||
ulong flags;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -119,10 +124,36 @@ struct irq_ops {
|
|||
* @return 0 if OK, or a negative error code.
|
||||
*/
|
||||
int (*free)(struct irq *irq);
|
||||
|
||||
#if CONFIG_IS_ENABLED(ACPIGEN)
|
||||
/**
|
||||
* get_acpi() - Get the ACPI info for an irq
|
||||
*
|
||||
* This converts a irq to an ACPI structure for adding to the ACPI
|
||||
* tables.
|
||||
*
|
||||
* @irq: irq to convert
|
||||
* @acpi_irq: Output ACPI interrupt information
|
||||
* @return ACPI pin number or -ve on error
|
||||
*/
|
||||
int (*get_acpi)(const struct irq *irq, struct acpi_irq *acpi_irq);
|
||||
#endif
|
||||
};
|
||||
|
||||
#define irq_get_ops(dev) ((struct irq_ops *)(dev)->driver->ops)
|
||||
|
||||
/**
|
||||
* irq_is_valid() - Check if an IRQ is valid
|
||||
*
|
||||
* @irq: IRQ description containing device and ID, e.g. previously
|
||||
* returned by irq_get_by_index()
|
||||
* @return true if valid, false if not
|
||||
*/
|
||||
static inline bool irq_is_valid(const struct irq *irq)
|
||||
{
|
||||
return irq->dev != NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* irq_route_pmc_gpio_gpe() - Get the GPIO for an event
|
||||
*
|
||||
|
@ -223,4 +254,16 @@ int irq_free(struct irq *irq);
|
|||
*/
|
||||
int irq_first_device_type(enum irq_dev_t type, struct udevice **devp);
|
||||
|
||||
/**
|
||||
* irq_get_acpi() - Get the ACPI info for an irq
|
||||
*
|
||||
* This converts a irq to an ACPI structure for adding to the ACPI
|
||||
* tables.
|
||||
*
|
||||
* @irq: irq to convert
|
||||
* @acpi_irq: Output ACPI interrupt information
|
||||
* @return ACPI pin number or -ve on error
|
||||
*/
|
||||
int irq_get_acpi(const struct irq *irq, struct acpi_irq *acpi_irq);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -31,13 +31,36 @@ struct p2sb_uc_priv {
|
|||
};
|
||||
|
||||
/**
|
||||
* struct p2sb_ops - Operations for the P2SB (none at present)
|
||||
* struct p2sb_ops - Operations for the P2SB
|
||||
*/
|
||||
struct p2sb_ops {
|
||||
/**
|
||||
* set_hide() - Set/clear the 'hide' bit on the p2sb
|
||||
*
|
||||
* This device can be hidden from the PCI bus if needed. This method
|
||||
* can be called before the p2sb is probed.
|
||||
*
|
||||
* @dev: P2SB device
|
||||
* @hide: true to hide the device, false to show it
|
||||
* @return 0 if OK, -ve on error
|
||||
*/
|
||||
int (*set_hide)(struct udevice *dev, bool hide);
|
||||
};
|
||||
|
||||
#define p2sb_get_ops(dev) ((struct p2sb_ops *)(dev)->driver->ops)
|
||||
|
||||
/**
|
||||
* p2sb_set_hide() - Set/clear the 'hide' bit on the p2sb
|
||||
*
|
||||
* This device can be hidden from the PCI bus if needed. This method
|
||||
* can be called before the p2sb is probed.
|
||||
*
|
||||
* @dev: P2SB device
|
||||
* @hide: true to hide the device, false to show it
|
||||
* @return 0 if OK, -ve on error
|
||||
*/
|
||||
int p2sb_set_hide(struct udevice *dev, bool hide);
|
||||
|
||||
/**
|
||||
* pcr_read32/16/8() - Read from a PCR device
|
||||
*
|
||||
|
@ -132,4 +155,13 @@ int p2sb_set_port_id(struct udevice *dev, int portid);
|
|||
*/
|
||||
int p2sb_get_port_id(struct udevice *dev);
|
||||
|
||||
/**
|
||||
* pcr_reg_address() Convert an offset in p2sb space to an absolute address
|
||||
*
|
||||
* @dev: Child device (whose parent is UCLASS_P2SB)
|
||||
* @offset: Offset within that child's address space
|
||||
* @return pointer to that offset within the child's address space
|
||||
*/
|
||||
void *pcr_reg_address(struct udevice *dev, uint offset);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -6,10 +6,22 @@
|
|||
#ifndef __ACPI_PMC_H
|
||||
#define __ACPI_PMC_H
|
||||
|
||||
#ifndef __ACPI__
|
||||
|
||||
enum {
|
||||
GPE0_REG_MAX = 4,
|
||||
};
|
||||
|
||||
enum {
|
||||
PM1_STS = 0x00,
|
||||
PM1_EN = 0x02,
|
||||
PM1_CNT = 0x04,
|
||||
PM1_TMR = 0x08,
|
||||
|
||||
GPE0_STS = 0x20,
|
||||
GPE0_EN = 0x30,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct acpi_pmc_upriv - holds common data for the x86 PMC
|
||||
*
|
||||
|
@ -182,4 +194,6 @@ void pmc_dump_info(struct udevice *dev);
|
|||
*/
|
||||
int pmc_gpe_init(struct udevice *dev);
|
||||
|
||||
#endif /* !__ACPI__ */
|
||||
|
||||
#endif
|
||||
|
|
|
@ -13,8 +13,8 @@
|
|||
#include <linux/bitops.h>
|
||||
|
||||
/* SPI mode flags */
|
||||
#define SPI_CPHA BIT(0) /* clock phase */
|
||||
#define SPI_CPOL BIT(1) /* clock polarity */
|
||||
#define SPI_CPHA BIT(0) /* clock phase (1 = SPI_CLOCK_PHASE_SECOND) */
|
||||
#define SPI_CPOL BIT(1) /* clock polarity (1 = SPI_POLARITY_HIGH) */
|
||||
#define SPI_MODE_0 (0|0) /* (original MicroWire) */
|
||||
#define SPI_MODE_1 (0|SPI_CPHA)
|
||||
#define SPI_MODE_2 (SPI_CPOL|0)
|
||||
|
|
|
@ -134,6 +134,23 @@ int ut_check_console_dump(struct unit_test_state *uts, int total_bytes);
|
|||
} \
|
||||
}
|
||||
|
||||
/*
|
||||
* Assert that two string expressions are equal, up to length of the
|
||||
* first
|
||||
*/
|
||||
#define ut_asserteq_strn(expr1, expr2) { \
|
||||
const char *_val1 = (expr1), *_val2 = (expr2); \
|
||||
int _len = strlen(_val1); \
|
||||
\
|
||||
if (memcmp(_val1, _val2, _len)) { \
|
||||
ut_failf(uts, __FILE__, __LINE__, __func__, \
|
||||
#expr1 " = " #expr2, \
|
||||
"Expected \"%.*s\", got \"%.*s\"", \
|
||||
_len, _val1, _len, _val2); \
|
||||
return CMD_RET_FAILURE; \
|
||||
} \
|
||||
}
|
||||
|
||||
/* Assert that two memory areas are equal */
|
||||
#define ut_asserteq_mem(expr1, expr2, len) { \
|
||||
const u8 *_val1 = (u8 *)(expr1), *_val2 = (u8 *)(expr2); \
|
||||
|
|
|
@ -17,6 +17,17 @@ unsigned long get_timer(unsigned long base);
|
|||
unsigned long timer_get_us(void);
|
||||
uint64_t get_timer_us(uint64_t base);
|
||||
|
||||
/**
|
||||
* get_timer_us_long() - Get the number of elapsed microseconds
|
||||
*
|
||||
* This uses 32-bit arithmetic on 32-bit machines, which is enough to handle
|
||||
* delays of over an hour. For 64-bit machines it uses a 64-bit value.
|
||||
*
|
||||
*@base: Base time to consider
|
||||
*@return elapsed time since @base
|
||||
*/
|
||||
unsigned long get_timer_us_long(unsigned long base);
|
||||
|
||||
/*
|
||||
* timer_test_add_offset()
|
||||
*
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
# SPDX-License-Identifier: GPL-2.0+
|
||||
#
|
||||
|
||||
obj-y += acpigen.o
|
||||
obj-y += acpi_device.o
|
||||
obj-y += acpi_dp.o
|
||||
obj-y += acpi_table.o
|
||||
|
|
823
lib/acpi/acpi_device.c
Normal file
823
lib/acpi/acpi_device.c
Normal file
|
@ -0,0 +1,823 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Generation of tables for particular device types
|
||||
*
|
||||
* Copyright 2019 Google LLC
|
||||
* Mostly taken from coreboot file of the same name
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <irq.h>
|
||||
#include <log.h>
|
||||
#include <usb.h>
|
||||
#include <acpi/acpigen.h>
|
||||
#include <acpi/acpi_device.h>
|
||||
#include <acpi/acpigen.h>
|
||||
#include <asm-generic/gpio.h>
|
||||
#include <dm/acpi.h>
|
||||
|
||||
/**
|
||||
* acpi_device_path_fill() - Find the root device and build a path from there
|
||||
*
|
||||
* This recursively reaches back to the root device and progressively adds path
|
||||
* elements until the device is reached.
|
||||
*
|
||||
* @dev: Device to return path of
|
||||
* @buf: Buffer to hold the path
|
||||
* @buf_len: Length of buffer
|
||||
* @cur: Current position in the buffer
|
||||
* @return new position in buffer after adding @dev, or -ve on error
|
||||
*/
|
||||
static int acpi_device_path_fill(const struct udevice *dev, char *buf,
|
||||
size_t buf_len, int cur)
|
||||
{
|
||||
char name[ACPI_NAME_MAX];
|
||||
int next = 0;
|
||||
int ret;
|
||||
|
||||
ret = acpi_get_name(dev, name);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Make sure this name segment will fit, including the path segment
|
||||
* separator and possible NULL terminator, if this is the last segment.
|
||||
*/
|
||||
if (cur + strlen(name) + 2 > buf_len)
|
||||
return -ENOSPC;
|
||||
|
||||
/* Walk up the tree to the root device */
|
||||
if (dev_get_parent(dev)) {
|
||||
next = acpi_device_path_fill(dev_get_parent(dev), buf, buf_len,
|
||||
cur);
|
||||
if (next < 0)
|
||||
return next;
|
||||
}
|
||||
|
||||
/* Fill in the path from the root device */
|
||||
next += snprintf(buf + next, buf_len - next, "%s%s",
|
||||
dev_get_parent(dev) && *name ? "." : "", name);
|
||||
|
||||
return next;
|
||||
}
|
||||
|
||||
int acpi_device_path(const struct udevice *dev, char *buf, int maxlen)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = acpi_device_path_fill(dev, buf, maxlen, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int acpi_device_scope(const struct udevice *dev, char *scope, int maxlen)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!dev_get_parent(dev))
|
||||
return log_msg_ret("noparent", -EINVAL);
|
||||
|
||||
ret = acpi_device_path_fill(dev_get_parent(dev), scope, maxlen, 0);
|
||||
if (ret < 0)
|
||||
return log_msg_ret("fill", ret);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
enum acpi_dev_status acpi_device_status(const struct udevice *dev)
|
||||
{
|
||||
return ACPI_DSTATUS_ALL_ON;
|
||||
}
|
||||
|
||||
/**
|
||||
* largeres_write_len_f() - Write a placeholder word value
|
||||
*
|
||||
* Write a forward length for a large resource (2 bytes)
|
||||
*
|
||||
* @return pointer to the zero word (for fixing up later)
|
||||
*/
|
||||
static void *largeres_write_len_f(struct acpi_ctx *ctx)
|
||||
{
|
||||
u8 *p = acpigen_get_current(ctx);
|
||||
|
||||
acpigen_emit_word(ctx, 0);
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
/**
|
||||
* largeres_fill_from_len() - Fill in a length value
|
||||
*
|
||||
* This calculated the number of bytes since the provided @start and writes it
|
||||
* to @ptr, which was previous returned by largeres_write_len_f().
|
||||
*
|
||||
* @ptr: Word to update
|
||||
* @start: Start address to count from to calculated the length
|
||||
*/
|
||||
static void largeres_fill_from_len(struct acpi_ctx *ctx, char *ptr, u8 *start)
|
||||
{
|
||||
u16 len = acpigen_get_current(ctx) - start;
|
||||
|
||||
ptr[0] = len & 0xff;
|
||||
ptr[1] = (len >> 8) & 0xff;
|
||||
}
|
||||
|
||||
/**
|
||||
* largeres_fill_len() - Fill in a length value, excluding the length itself
|
||||
*
|
||||
* Fill in the length field with the value calculated from after the 16bit
|
||||
* field to acpigen current. This is useful since the length value does not
|
||||
* include the length field itself.
|
||||
*
|
||||
* This calls acpi_device_largeres_fill_len() passing @ptr + 2 as @start
|
||||
*
|
||||
* @ptr: Word to update.
|
||||
*/
|
||||
static void largeres_fill_len(struct acpi_ctx *ctx, void *ptr)
|
||||
{
|
||||
largeres_fill_from_len(ctx, ptr, ptr + sizeof(u16));
|
||||
}
|
||||
|
||||
/* ACPI 6.3 section 6.4.3.6: Extended Interrupt Descriptor */
|
||||
static int acpi_device_write_interrupt(struct acpi_ctx *ctx,
|
||||
const struct acpi_irq *irq)
|
||||
{
|
||||
void *desc_length;
|
||||
u8 flags;
|
||||
|
||||
if (!irq->pin)
|
||||
return -ENOENT;
|
||||
|
||||
/* This is supported by GpioInt() but not Interrupt() */
|
||||
if (irq->polarity == ACPI_IRQ_ACTIVE_BOTH)
|
||||
return -EINVAL;
|
||||
|
||||
/* Byte 0: Descriptor Type */
|
||||
acpigen_emit_byte(ctx, ACPI_DESCRIPTOR_INTERRUPT);
|
||||
|
||||
/* Byte 1-2: Length (filled in later) */
|
||||
desc_length = largeres_write_len_f(ctx);
|
||||
|
||||
/*
|
||||
* Byte 3: Flags
|
||||
* [7:5]: Reserved
|
||||
* [4]: Wake (0=NO_WAKE 1=WAKE)
|
||||
* [3]: Sharing (0=EXCLUSIVE 1=SHARED)
|
||||
* [2]: Polarity (0=HIGH 1=LOW)
|
||||
* [1]: Mode (0=LEVEL 1=EDGE)
|
||||
* [0]: Resource (0=PRODUCER 1=CONSUMER)
|
||||
*/
|
||||
flags = BIT(0); /* ResourceConsumer */
|
||||
if (irq->mode == ACPI_IRQ_EDGE_TRIGGERED)
|
||||
flags |= BIT(1);
|
||||
if (irq->polarity == ACPI_IRQ_ACTIVE_LOW)
|
||||
flags |= BIT(2);
|
||||
if (irq->shared == ACPI_IRQ_SHARED)
|
||||
flags |= BIT(3);
|
||||
if (irq->wake == ACPI_IRQ_WAKE)
|
||||
flags |= BIT(4);
|
||||
acpigen_emit_byte(ctx, flags);
|
||||
|
||||
/* Byte 4: Interrupt Table Entry Count */
|
||||
acpigen_emit_byte(ctx, 1);
|
||||
|
||||
/* Byte 5-8: Interrupt Number */
|
||||
acpigen_emit_dword(ctx, irq->pin);
|
||||
|
||||
/* Fill in Descriptor Length (account for len word) */
|
||||
largeres_fill_len(ctx, desc_length);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int acpi_device_write_interrupt_irq(struct acpi_ctx *ctx,
|
||||
const struct irq *req_irq)
|
||||
{
|
||||
struct acpi_irq irq;
|
||||
int ret;
|
||||
|
||||
ret = irq_get_acpi(req_irq, &irq);
|
||||
if (ret)
|
||||
return log_msg_ret("get", ret);
|
||||
ret = acpi_device_write_interrupt(ctx, &irq);
|
||||
if (ret)
|
||||
return log_msg_ret("write", ret);
|
||||
|
||||
return irq.pin;
|
||||
}
|
||||
|
||||
/* ACPI 6.3 section 6.4.3.8.1 - GPIO Interrupt or I/O */
|
||||
int acpi_device_write_gpio(struct acpi_ctx *ctx, const struct acpi_gpio *gpio)
|
||||
{
|
||||
void *start, *desc_length;
|
||||
void *pin_table_offset, *vendor_data_offset, *resource_offset;
|
||||
u16 flags = 0;
|
||||
int pin;
|
||||
|
||||
if (gpio->type > ACPI_GPIO_TYPE_IO)
|
||||
return -EINVAL;
|
||||
|
||||
start = acpigen_get_current(ctx);
|
||||
|
||||
/* Byte 0: Descriptor Type */
|
||||
acpigen_emit_byte(ctx, ACPI_DESCRIPTOR_GPIO);
|
||||
|
||||
/* Byte 1-2: Length (fill in later) */
|
||||
desc_length = largeres_write_len_f(ctx);
|
||||
|
||||
/* Byte 3: Revision ID */
|
||||
acpigen_emit_byte(ctx, ACPI_GPIO_REVISION_ID);
|
||||
|
||||
/* Byte 4: GpioIo or GpioInt */
|
||||
acpigen_emit_byte(ctx, gpio->type);
|
||||
|
||||
/*
|
||||
* Byte 5-6: General Flags
|
||||
* [15:1]: 0 => Reserved
|
||||
* [0]: 1 => ResourceConsumer
|
||||
*/
|
||||
acpigen_emit_word(ctx, 1 << 0);
|
||||
|
||||
switch (gpio->type) {
|
||||
case ACPI_GPIO_TYPE_INTERRUPT:
|
||||
/*
|
||||
* Byte 7-8: GPIO Interrupt Flags
|
||||
* [15:5]: 0 => Reserved
|
||||
* [4]: Wake (0=NO_WAKE 1=WAKE)
|
||||
* [3]: Sharing (0=EXCLUSIVE 1=SHARED)
|
||||
* [2:1]: Polarity (0=HIGH 1=LOW 2=BOTH)
|
||||
* [0]: Mode (0=LEVEL 1=EDGE)
|
||||
*/
|
||||
if (gpio->irq.mode == ACPI_IRQ_EDGE_TRIGGERED)
|
||||
flags |= 1 << 0;
|
||||
if (gpio->irq.shared == ACPI_IRQ_SHARED)
|
||||
flags |= 1 << 3;
|
||||
if (gpio->irq.wake == ACPI_IRQ_WAKE)
|
||||
flags |= 1 << 4;
|
||||
|
||||
switch (gpio->irq.polarity) {
|
||||
case ACPI_IRQ_ACTIVE_HIGH:
|
||||
flags |= 0 << 1;
|
||||
break;
|
||||
case ACPI_IRQ_ACTIVE_LOW:
|
||||
flags |= 1 << 1;
|
||||
break;
|
||||
case ACPI_IRQ_ACTIVE_BOTH:
|
||||
flags |= 2 << 1;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case ACPI_GPIO_TYPE_IO:
|
||||
/*
|
||||
* Byte 7-8: GPIO IO Flags
|
||||
* [15:4]: 0 => Reserved
|
||||
* [3]: Sharing (0=EXCLUSIVE 1=SHARED)
|
||||
* [2]: 0 => Reserved
|
||||
* [1:0]: IO Restriction
|
||||
* 0 => IoRestrictionNone
|
||||
* 1 => IoRestrictionInputOnly
|
||||
* 2 => IoRestrictionOutputOnly
|
||||
* 3 => IoRestrictionNoneAndPreserve
|
||||
*/
|
||||
flags |= gpio->io_restrict & 3;
|
||||
if (gpio->io_shared)
|
||||
flags |= 1 << 3;
|
||||
break;
|
||||
}
|
||||
acpigen_emit_word(ctx, flags);
|
||||
|
||||
/*
|
||||
* Byte 9: Pin Configuration
|
||||
* 0x01 => Default (no configuration applied)
|
||||
* 0x02 => Pull-up
|
||||
* 0x03 => Pull-down
|
||||
* 0x04-0x7F => Reserved
|
||||
* 0x80-0xff => Vendor defined
|
||||
*/
|
||||
acpigen_emit_byte(ctx, gpio->pull);
|
||||
|
||||
/* Byte 10-11: Output Drive Strength in 1/100 mA */
|
||||
acpigen_emit_word(ctx, gpio->output_drive_strength);
|
||||
|
||||
/* Byte 12-13: Debounce Timeout in 1/100 ms */
|
||||
acpigen_emit_word(ctx, gpio->interrupt_debounce_timeout);
|
||||
|
||||
/* Byte 14-15: Pin Table Offset, relative to start */
|
||||
pin_table_offset = largeres_write_len_f(ctx);
|
||||
|
||||
/* Byte 16: Reserved */
|
||||
acpigen_emit_byte(ctx, 0);
|
||||
|
||||
/* Byte 17-18: Resource Source Name Offset, relative to start */
|
||||
resource_offset = largeres_write_len_f(ctx);
|
||||
|
||||
/* Byte 19-20: Vendor Data Offset, relative to start */
|
||||
vendor_data_offset = largeres_write_len_f(ctx);
|
||||
|
||||
/* Byte 21-22: Vendor Data Length */
|
||||
acpigen_emit_word(ctx, 0);
|
||||
|
||||
/* Fill in Pin Table Offset */
|
||||
largeres_fill_from_len(ctx, pin_table_offset, start);
|
||||
|
||||
/* Pin Table, one word for each pin */
|
||||
for (pin = 0; pin < gpio->pin_count; pin++)
|
||||
acpigen_emit_word(ctx, gpio->pins[pin]);
|
||||
|
||||
/* Fill in Resource Source Name Offset */
|
||||
largeres_fill_from_len(ctx, resource_offset, start);
|
||||
|
||||
/* Resource Source Name String */
|
||||
acpigen_emit_string(ctx, gpio->resource);
|
||||
|
||||
/* Fill in Vendor Data Offset */
|
||||
largeres_fill_from_len(ctx, vendor_data_offset, start);
|
||||
|
||||
/* Fill in GPIO Descriptor Length (account for len word) */
|
||||
largeres_fill_len(ctx, desc_length);
|
||||
|
||||
return gpio->pins[0];
|
||||
}
|
||||
|
||||
int acpi_device_write_gpio_desc(struct acpi_ctx *ctx,
|
||||
const struct gpio_desc *desc)
|
||||
{
|
||||
struct acpi_gpio gpio;
|
||||
int ret;
|
||||
|
||||
ret = gpio_get_acpi(desc, &gpio);
|
||||
if (ret)
|
||||
return log_msg_ret("desc", ret);
|
||||
ret = acpi_device_write_gpio(ctx, &gpio);
|
||||
if (ret < 0)
|
||||
return log_msg_ret("gpio", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int acpi_device_write_interrupt_or_gpio(struct acpi_ctx *ctx,
|
||||
struct udevice *dev, const char *prop)
|
||||
{
|
||||
struct irq req_irq;
|
||||
int pin;
|
||||
int ret;
|
||||
|
||||
ret = irq_get_by_index(dev, 0, &req_irq);
|
||||
if (!ret) {
|
||||
ret = acpi_device_write_interrupt_irq(ctx, &req_irq);
|
||||
if (ret < 0)
|
||||
return log_msg_ret("irq", ret);
|
||||
pin = ret;
|
||||
} else {
|
||||
struct gpio_desc req_gpio;
|
||||
|
||||
ret = gpio_request_by_name(dev, prop, 0, &req_gpio,
|
||||
GPIOD_IS_IN);
|
||||
if (ret)
|
||||
return log_msg_ret("no gpio", ret);
|
||||
ret = acpi_device_write_gpio_desc(ctx, &req_gpio);
|
||||
if (ret < 0)
|
||||
return log_msg_ret("gpio", ret);
|
||||
pin = ret;
|
||||
}
|
||||
|
||||
return pin;
|
||||
}
|
||||
|
||||
/* PowerResource() with Enable and/or Reset control */
|
||||
int acpi_device_add_power_res(struct acpi_ctx *ctx, u32 tx_state_val,
|
||||
const char *dw0_read, const char *dw0_write,
|
||||
const struct gpio_desc *reset_gpio,
|
||||
uint reset_delay_ms, uint reset_off_delay_ms,
|
||||
const struct gpio_desc *enable_gpio,
|
||||
uint enable_delay_ms, uint enable_off_delay_ms,
|
||||
const struct gpio_desc *stop_gpio,
|
||||
uint stop_delay_ms, uint stop_off_delay_ms)
|
||||
{
|
||||
static const char *const power_res_dev_states[] = { "_PR0", "_PR3" };
|
||||
struct acpi_gpio reset, enable, stop;
|
||||
bool has_reset, has_enable, has_stop;
|
||||
int ret;
|
||||
|
||||
gpio_get_acpi(reset_gpio, &reset);
|
||||
gpio_get_acpi(enable_gpio, &enable);
|
||||
gpio_get_acpi(stop_gpio, &stop);
|
||||
has_reset = reset.pins[0];
|
||||
has_enable = enable.pins[0];
|
||||
has_stop = stop.pins[0];
|
||||
|
||||
if (!has_reset && !has_enable && !has_stop)
|
||||
return -EINVAL;
|
||||
|
||||
/* PowerResource (PRIC, 0, 0) */
|
||||
acpigen_write_power_res(ctx, "PRIC", 0, 0, power_res_dev_states,
|
||||
ARRAY_SIZE(power_res_dev_states));
|
||||
|
||||
/* Method (_STA, 0, NotSerialized) { Return (0x1) } */
|
||||
acpigen_write_sta(ctx, 0x1);
|
||||
|
||||
/* Method (_ON, 0, Serialized) */
|
||||
acpigen_write_method_serialized(ctx, "_ON", 0);
|
||||
if (reset_gpio) {
|
||||
ret = acpigen_set_enable_tx_gpio(ctx, tx_state_val, dw0_read,
|
||||
dw0_write, &reset, true);
|
||||
if (ret)
|
||||
return log_msg_ret("reset1", ret);
|
||||
}
|
||||
if (has_enable) {
|
||||
ret = acpigen_set_enable_tx_gpio(ctx, tx_state_val, dw0_read,
|
||||
dw0_write, &enable, true);
|
||||
if (ret)
|
||||
return log_msg_ret("enable1", ret);
|
||||
if (enable_delay_ms)
|
||||
acpigen_write_sleep(ctx, enable_delay_ms);
|
||||
}
|
||||
if (has_reset) {
|
||||
ret = acpigen_set_enable_tx_gpio(ctx, tx_state_val, dw0_read,
|
||||
dw0_write, &reset, false);
|
||||
if (ret)
|
||||
return log_msg_ret("reset2", ret);
|
||||
if (reset_delay_ms)
|
||||
acpigen_write_sleep(ctx, reset_delay_ms);
|
||||
}
|
||||
if (has_stop) {
|
||||
ret = acpigen_set_enable_tx_gpio(ctx, tx_state_val, dw0_read,
|
||||
dw0_write, &stop, false);
|
||||
if (ret)
|
||||
return log_msg_ret("stop1", ret);
|
||||
if (stop_delay_ms)
|
||||
acpigen_write_sleep(ctx, stop_delay_ms);
|
||||
}
|
||||
acpigen_pop_len(ctx); /* _ON method */
|
||||
|
||||
/* Method (_OFF, 0, Serialized) */
|
||||
acpigen_write_method_serialized(ctx, "_OFF", 0);
|
||||
if (has_stop) {
|
||||
ret = acpigen_set_enable_tx_gpio(ctx, tx_state_val, dw0_read,
|
||||
dw0_write, &stop, true);
|
||||
if (ret)
|
||||
return log_msg_ret("stop2", ret);
|
||||
if (stop_off_delay_ms)
|
||||
acpigen_write_sleep(ctx, stop_off_delay_ms);
|
||||
}
|
||||
if (has_reset) {
|
||||
ret = acpigen_set_enable_tx_gpio(ctx, tx_state_val, dw0_read,
|
||||
dw0_write, &reset, true);
|
||||
if (ret)
|
||||
return log_msg_ret("reset3", ret);
|
||||
if (reset_off_delay_ms)
|
||||
acpigen_write_sleep(ctx, reset_off_delay_ms);
|
||||
}
|
||||
if (has_enable) {
|
||||
ret = acpigen_set_enable_tx_gpio(ctx, tx_state_val, dw0_read,
|
||||
dw0_write, &enable, false);
|
||||
if (ret)
|
||||
return log_msg_ret("enable2", ret);
|
||||
if (enable_off_delay_ms)
|
||||
acpigen_write_sleep(ctx, enable_off_delay_ms);
|
||||
}
|
||||
acpigen_pop_len(ctx); /* _OFF method */
|
||||
|
||||
acpigen_pop_len(ctx); /* PowerResource PRIC */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ACPI 6.3 section 6.4.3.8.2.1 - I2cSerialBus() */
|
||||
static void acpi_device_write_i2c(struct acpi_ctx *ctx,
|
||||
const struct acpi_i2c *i2c)
|
||||
{
|
||||
void *desc_length, *type_length;
|
||||
|
||||
/* Byte 0: Descriptor Type */
|
||||
acpigen_emit_byte(ctx, ACPI_DESCRIPTOR_SERIAL_BUS);
|
||||
|
||||
/* Byte 1+2: Length (filled in later) */
|
||||
desc_length = largeres_write_len_f(ctx);
|
||||
|
||||
/* Byte 3: Revision ID */
|
||||
acpigen_emit_byte(ctx, ACPI_I2C_SERIAL_BUS_REVISION_ID);
|
||||
|
||||
/* Byte 4: Resource Source Index is Reserved */
|
||||
acpigen_emit_byte(ctx, 0);
|
||||
|
||||
/* Byte 5: Serial Bus Type is I2C */
|
||||
acpigen_emit_byte(ctx, ACPI_SERIAL_BUS_TYPE_I2C);
|
||||
|
||||
/*
|
||||
* Byte 6: Flags
|
||||
* [7:2]: 0 => Reserved
|
||||
* [1]: 1 => ResourceConsumer
|
||||
* [0]: 0 => ControllerInitiated
|
||||
*/
|
||||
acpigen_emit_byte(ctx, 1 << 1);
|
||||
|
||||
/*
|
||||
* Byte 7-8: Type Specific Flags
|
||||
* [15:1]: 0 => Reserved
|
||||
* [0]: 0 => 7bit, 1 => 10bit
|
||||
*/
|
||||
acpigen_emit_word(ctx, i2c->mode_10bit);
|
||||
|
||||
/* Byte 9: Type Specific Revision ID */
|
||||
acpigen_emit_byte(ctx, ACPI_I2C_TYPE_SPECIFIC_REVISION_ID);
|
||||
|
||||
/* Byte 10-11: I2C Type Data Length */
|
||||
type_length = largeres_write_len_f(ctx);
|
||||
|
||||
/* Byte 12-15: I2C Bus Speed */
|
||||
acpigen_emit_dword(ctx, i2c->speed);
|
||||
|
||||
/* Byte 16-17: I2C Slave Address */
|
||||
acpigen_emit_word(ctx, i2c->address);
|
||||
|
||||
/* Fill in Type Data Length */
|
||||
largeres_fill_len(ctx, type_length);
|
||||
|
||||
/* Byte 18+: ResourceSource */
|
||||
acpigen_emit_string(ctx, i2c->resource);
|
||||
|
||||
/* Fill in I2C Descriptor Length */
|
||||
largeres_fill_len(ctx, desc_length);
|
||||
}
|
||||
|
||||
/**
|
||||
* acpi_device_set_i2c() - Set up an ACPI I2C struct from a device
|
||||
*
|
||||
* The value of @scope is not copied, but only referenced. This implies the
|
||||
* caller has to ensure it stays valid for the lifetime of @i2c.
|
||||
*
|
||||
* @dev: I2C device to convert
|
||||
* @i2c: Place to put the new structure
|
||||
* @scope: Scope of the I2C device (this is the controller path)
|
||||
* @return chip address of device
|
||||
*/
|
||||
static int acpi_device_set_i2c(const struct udevice *dev, struct acpi_i2c *i2c,
|
||||
const char *scope)
|
||||
{
|
||||
struct dm_i2c_chip *chip = dev_get_parent_platdata(dev);
|
||||
struct udevice *bus = dev_get_parent(dev);
|
||||
|
||||
memset(i2c, '\0', sizeof(*i2c));
|
||||
i2c->address = chip->chip_addr;
|
||||
i2c->mode_10bit = 0;
|
||||
|
||||
/*
|
||||
* i2c_bus->speed_hz is set if this device is probed, but if not we
|
||||
* must use the device tree
|
||||
*/
|
||||
i2c->speed = dev_read_u32_default(bus, "clock-frequency",
|
||||
I2C_SPEED_STANDARD_RATE);
|
||||
i2c->resource = scope;
|
||||
|
||||
return i2c->address;
|
||||
}
|
||||
|
||||
int acpi_device_write_i2c_dev(struct acpi_ctx *ctx, const struct udevice *dev)
|
||||
{
|
||||
char scope[ACPI_PATH_MAX];
|
||||
struct acpi_i2c i2c;
|
||||
int ret;
|
||||
|
||||
ret = acpi_device_scope(dev, scope, sizeof(scope));
|
||||
if (ret)
|
||||
return log_msg_ret("scope", ret);
|
||||
ret = acpi_device_set_i2c(dev, &i2c, scope);
|
||||
if (ret < 0)
|
||||
return log_msg_ret("set", ret);
|
||||
acpi_device_write_i2c(ctx, &i2c);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SPI
|
||||
/* ACPI 6.1 section 6.4.3.8.2.2 - SpiSerialBus() */
|
||||
static void acpi_device_write_spi(struct acpi_ctx *ctx, const struct acpi_spi *spi)
|
||||
{
|
||||
void *desc_length, *type_length;
|
||||
u16 flags = 0;
|
||||
|
||||
/* Byte 0: Descriptor Type */
|
||||
acpigen_emit_byte(ctx, ACPI_DESCRIPTOR_SERIAL_BUS);
|
||||
|
||||
/* Byte 1+2: Length (filled in later) */
|
||||
desc_length = largeres_write_len_f(ctx);
|
||||
|
||||
/* Byte 3: Revision ID */
|
||||
acpigen_emit_byte(ctx, ACPI_SPI_SERIAL_BUS_REVISION_ID);
|
||||
|
||||
/* Byte 4: Resource Source Index is Reserved */
|
||||
acpigen_emit_byte(ctx, 0);
|
||||
|
||||
/* Byte 5: Serial Bus Type is SPI */
|
||||
acpigen_emit_byte(ctx, ACPI_SERIAL_BUS_TYPE_SPI);
|
||||
|
||||
/*
|
||||
* Byte 6: Flags
|
||||
* [7:2]: 0 => Reserved
|
||||
* [1]: 1 => ResourceConsumer
|
||||
* [0]: 0 => ControllerInitiated
|
||||
*/
|
||||
acpigen_emit_byte(ctx, BIT(1));
|
||||
|
||||
/*
|
||||
* Byte 7-8: Type Specific Flags
|
||||
* [15:2]: 0 => Reserveda
|
||||
* [1]: 0 => ActiveLow, 1 => ActiveHigh
|
||||
* [0]: 0 => FourWire, 1 => ThreeWire
|
||||
*/
|
||||
if (spi->wire_mode == SPI_3_WIRE_MODE)
|
||||
flags |= BIT(0);
|
||||
if (spi->device_select_polarity == SPI_POLARITY_HIGH)
|
||||
flags |= BIT(1);
|
||||
acpigen_emit_word(ctx, flags);
|
||||
|
||||
/* Byte 9: Type Specific Revision ID */
|
||||
acpigen_emit_byte(ctx, ACPI_SPI_TYPE_SPECIFIC_REVISION_ID);
|
||||
|
||||
/* Byte 10-11: SPI Type Data Length */
|
||||
type_length = largeres_write_len_f(ctx);
|
||||
|
||||
/* Byte 12-15: Connection Speed */
|
||||
acpigen_emit_dword(ctx, spi->speed);
|
||||
|
||||
/* Byte 16: Data Bit Length */
|
||||
acpigen_emit_byte(ctx, spi->data_bit_length);
|
||||
|
||||
/* Byte 17: Clock Phase */
|
||||
acpigen_emit_byte(ctx, spi->clock_phase);
|
||||
|
||||
/* Byte 18: Clock Polarity */
|
||||
acpigen_emit_byte(ctx, spi->clock_polarity);
|
||||
|
||||
/* Byte 19-20: Device Selection */
|
||||
acpigen_emit_word(ctx, spi->device_select);
|
||||
|
||||
/* Fill in Type Data Length */
|
||||
largeres_fill_len(ctx, type_length);
|
||||
|
||||
/* Byte 21+: ResourceSource String */
|
||||
acpigen_emit_string(ctx, spi->resource);
|
||||
|
||||
/* Fill in SPI Descriptor Length */
|
||||
largeres_fill_len(ctx, desc_length);
|
||||
}
|
||||
|
||||
/**
|
||||
* acpi_device_set_spi() - Set up an ACPI SPI struct from a device
|
||||
*
|
||||
* The value of @scope is not copied, but only referenced. This implies the
|
||||
* caller has to ensure it stays valid for the lifetime of @spi.
|
||||
*
|
||||
* @dev: SPI device to convert
|
||||
* @spi: Place to put the new structure
|
||||
* @scope: Scope of the SPI device (this is the controller path)
|
||||
* @return 0 (always)
|
||||
*/
|
||||
static int acpi_device_set_spi(const struct udevice *dev, struct acpi_spi *spi,
|
||||
const char *scope)
|
||||
{
|
||||
struct dm_spi_slave_platdata *plat;
|
||||
struct spi_slave *slave = dev_get_parent_priv(dev);
|
||||
|
||||
plat = dev_get_parent_platdata(slave->dev);
|
||||
memset(spi, '\0', sizeof(*spi));
|
||||
spi->device_select = plat->cs;
|
||||
spi->device_select_polarity = SPI_POLARITY_LOW;
|
||||
spi->wire_mode = SPI_4_WIRE_MODE;
|
||||
spi->speed = plat->max_hz;
|
||||
spi->data_bit_length = slave->wordlen;
|
||||
spi->clock_phase = plat->mode & SPI_CPHA ?
|
||||
SPI_CLOCK_PHASE_SECOND : SPI_CLOCK_PHASE_FIRST;
|
||||
spi->clock_polarity = plat->mode & SPI_CPOL ?
|
||||
SPI_POLARITY_HIGH : SPI_POLARITY_LOW;
|
||||
spi->resource = scope;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int acpi_device_write_spi_dev(struct acpi_ctx *ctx, const struct udevice *dev)
|
||||
{
|
||||
char scope[ACPI_PATH_MAX];
|
||||
struct acpi_spi spi;
|
||||
int ret;
|
||||
|
||||
ret = acpi_device_scope(dev, scope, sizeof(scope));
|
||||
if (ret)
|
||||
return log_msg_ret("scope", ret);
|
||||
ret = acpi_device_set_spi(dev, &spi, scope);
|
||||
if (ret)
|
||||
return log_msg_ret("set", ret);
|
||||
acpi_device_write_spi(ctx, &spi);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_SPI */
|
||||
|
||||
static const char *acpi_name_from_id(enum uclass_id id)
|
||||
{
|
||||
switch (id) {
|
||||
case UCLASS_USB_HUB:
|
||||
/* Root Hub */
|
||||
return "RHUB";
|
||||
/* DSDT: acpi/northbridge.asl */
|
||||
case UCLASS_NORTHBRIDGE:
|
||||
return "MCHC";
|
||||
/* DSDT: acpi/lpc.asl */
|
||||
case UCLASS_LPC:
|
||||
return "LPCB";
|
||||
/* DSDT: acpi/xhci.asl */
|
||||
case UCLASS_USB:
|
||||
/* This only supports USB3.0 controllers at present */
|
||||
return "XHCI";
|
||||
case UCLASS_PWM:
|
||||
return "PWM";
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int acpi_check_seq(const struct udevice *dev)
|
||||
{
|
||||
if (dev->req_seq == -1) {
|
||||
log_warning("Device '%s' has no seq\n", dev->name);
|
||||
return log_msg_ret("no seq", -ENXIO);
|
||||
}
|
||||
|
||||
return dev->req_seq;
|
||||
}
|
||||
|
||||
/* If you change this function, add test cases to dm_test_acpi_get_name() */
|
||||
int acpi_device_infer_name(const struct udevice *dev, char *out_name)
|
||||
{
|
||||
enum uclass_id parent_id = UCLASS_INVALID;
|
||||
enum uclass_id id;
|
||||
const char *name = NULL;
|
||||
|
||||
id = device_get_uclass_id(dev);
|
||||
if (dev_get_parent(dev))
|
||||
parent_id = device_get_uclass_id(dev_get_parent(dev));
|
||||
|
||||
if (id == UCLASS_SOUND)
|
||||
name = "HDAS";
|
||||
else if (id == UCLASS_PCI)
|
||||
name = "PCI0";
|
||||
else if (device_is_on_pci_bus(dev))
|
||||
name = acpi_name_from_id(id);
|
||||
if (!name) {
|
||||
switch (parent_id) {
|
||||
case UCLASS_USB: {
|
||||
struct usb_device *udev = dev_get_parent_priv(dev);
|
||||
|
||||
sprintf(out_name, udev->speed >= USB_SPEED_SUPER ?
|
||||
"HS%02d" : "FS%02d", udev->portnr);
|
||||
name = out_name;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!name) {
|
||||
int num;
|
||||
|
||||
switch (id) {
|
||||
/* DSDT: acpi/lpss.asl */
|
||||
case UCLASS_SERIAL:
|
||||
num = acpi_check_seq(dev);
|
||||
if (num < 0)
|
||||
return num;
|
||||
sprintf(out_name, "URT%d", num);
|
||||
name = out_name;
|
||||
break;
|
||||
case UCLASS_I2C:
|
||||
num = acpi_check_seq(dev);
|
||||
if (num < 0)
|
||||
return num;
|
||||
sprintf(out_name, "I2C%d", num);
|
||||
name = out_name;
|
||||
break;
|
||||
case UCLASS_SPI:
|
||||
num = acpi_check_seq(dev);
|
||||
if (num < 0)
|
||||
return num;
|
||||
sprintf(out_name, "SPI%d", num);
|
||||
name = out_name;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!name) {
|
||||
log_warning("No name for device '%s'\n", dev->name);
|
||||
return -ENOENT;
|
||||
}
|
||||
if (name != out_name)
|
||||
acpi_copy_name(out_name, name);
|
||||
|
||||
return 0;
|
||||
}
|
402
lib/acpi/acpi_dp.c
Normal file
402
lib/acpi/acpi_dp.c
Normal file
|
@ -0,0 +1,402 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Generation of tables for particular device types
|
||||
*
|
||||
* Copyright 2019 Google LLC
|
||||
* Mostly taken from coreboot file acpi_device.c
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <log.h>
|
||||
#include <malloc.h>
|
||||
#include <uuid.h>
|
||||
#include <acpi/acpigen.h>
|
||||
#include <acpi/acpi_dp.h>
|
||||
#include <dm/acpi.h>
|
||||
|
||||
static void acpi_dp_write_array(struct acpi_ctx *ctx,
|
||||
const struct acpi_dp *array);
|
||||
|
||||
static void acpi_dp_write_value(struct acpi_ctx *ctx,
|
||||
const struct acpi_dp *prop)
|
||||
{
|
||||
switch (prop->type) {
|
||||
case ACPI_DP_TYPE_INTEGER:
|
||||
acpigen_write_integer(ctx, prop->integer);
|
||||
break;
|
||||
case ACPI_DP_TYPE_STRING:
|
||||
case ACPI_DP_TYPE_CHILD:
|
||||
acpigen_write_string(ctx, prop->string);
|
||||
break;
|
||||
case ACPI_DP_TYPE_REFERENCE:
|
||||
acpigen_emit_namestring(ctx, prop->string);
|
||||
break;
|
||||
case ACPI_DP_TYPE_ARRAY:
|
||||
acpi_dp_write_array(ctx, prop->array);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Package (2) { "prop->name", VALUE } */
|
||||
static void acpi_dp_write_property(struct acpi_ctx *ctx,
|
||||
const struct acpi_dp *prop)
|
||||
{
|
||||
acpigen_write_package(ctx, 2);
|
||||
acpigen_write_string(ctx, prop->name);
|
||||
acpi_dp_write_value(ctx, prop);
|
||||
acpigen_pop_len(ctx);
|
||||
}
|
||||
|
||||
/* Write array of Device Properties */
|
||||
static void acpi_dp_write_array(struct acpi_ctx *ctx,
|
||||
const struct acpi_dp *array)
|
||||
{
|
||||
const struct acpi_dp *dp;
|
||||
char *pkg_count;
|
||||
|
||||
/* Package element count determined as it is populated */
|
||||
pkg_count = acpigen_write_package(ctx, 0);
|
||||
|
||||
/*
|
||||
* Only acpi_dp of type DP_TYPE_TABLE is allowed to be an array.
|
||||
* DP_TYPE_TABLE does not have a value to be written. Thus, start
|
||||
* the loop from next type in the array.
|
||||
*/
|
||||
for (dp = array->next; dp; dp = dp->next) {
|
||||
acpi_dp_write_value(ctx, dp);
|
||||
(*pkg_count)++;
|
||||
}
|
||||
|
||||
acpigen_pop_len(ctx);
|
||||
}
|
||||
|
||||
static void acpi_dp_free(struct acpi_dp *dp)
|
||||
{
|
||||
assert(dp);
|
||||
while (dp) {
|
||||
struct acpi_dp *p = dp->next;
|
||||
|
||||
switch (dp->type) {
|
||||
case ACPI_DP_TYPE_CHILD:
|
||||
acpi_dp_free(dp->child);
|
||||
break;
|
||||
case ACPI_DP_TYPE_ARRAY:
|
||||
acpi_dp_free(dp->array);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
free(dp);
|
||||
dp = p;
|
||||
}
|
||||
}
|
||||
|
||||
static int acpi_dp_write_internal(struct acpi_ctx *ctx, struct acpi_dp *table)
|
||||
{
|
||||
struct acpi_dp *dp, *prop;
|
||||
char *dp_count, *prop_count = NULL;
|
||||
int child_count = 0;
|
||||
int ret;
|
||||
|
||||
assert(table);
|
||||
if (table->type != ACPI_DP_TYPE_TABLE)
|
||||
return 0;
|
||||
|
||||
/* Name (name) */
|
||||
acpigen_write_name(ctx, table->name);
|
||||
|
||||
/* Device Property list starts with the next entry */
|
||||
prop = table->next;
|
||||
|
||||
/* Package (DP), default to assuming no properties or children */
|
||||
dp_count = acpigen_write_package(ctx, 0);
|
||||
|
||||
/* Print base properties */
|
||||
for (dp = prop; dp; dp = dp->next) {
|
||||
if (dp->type == ACPI_DP_TYPE_CHILD) {
|
||||
child_count++;
|
||||
} else {
|
||||
/*
|
||||
* The UUID and package is only added when
|
||||
* we come across the first property. This
|
||||
* is to avoid creating a zero-length package
|
||||
* in situations where there are only children.
|
||||
*/
|
||||
if (!prop_count) {
|
||||
*dp_count += 2;
|
||||
/* ToUUID (ACPI_DP_UUID) */
|
||||
ret = acpigen_write_uuid(ctx, ACPI_DP_UUID);
|
||||
if (ret)
|
||||
return log_msg_ret("touuid", ret);
|
||||
/*
|
||||
* Package (PROP), element count determined as
|
||||
* it is populated
|
||||
*/
|
||||
prop_count = acpigen_write_package(ctx, 0);
|
||||
}
|
||||
(*prop_count)++;
|
||||
acpi_dp_write_property(ctx, dp);
|
||||
}
|
||||
}
|
||||
|
||||
if (prop_count) {
|
||||
/* Package (PROP) length, if a package was written */
|
||||
acpigen_pop_len(ctx);
|
||||
}
|
||||
|
||||
if (child_count) {
|
||||
/* Update DP package count to 2 or 4 */
|
||||
*dp_count += 2;
|
||||
/* ToUUID (ACPI_DP_CHILD_UUID) */
|
||||
ret = acpigen_write_uuid(ctx, ACPI_DP_CHILD_UUID);
|
||||
if (ret)
|
||||
return log_msg_ret("child uuid", ret);
|
||||
|
||||
/* Print child pointer properties */
|
||||
acpigen_write_package(ctx, child_count);
|
||||
|
||||
for (dp = prop; dp; dp = dp->next)
|
||||
if (dp->type == ACPI_DP_TYPE_CHILD)
|
||||
acpi_dp_write_property(ctx, dp);
|
||||
/* Package (CHILD) length */
|
||||
acpigen_pop_len(ctx);
|
||||
}
|
||||
|
||||
/* Package (DP) length */
|
||||
acpigen_pop_len(ctx);
|
||||
|
||||
/* Recursively parse children into separate tables */
|
||||
for (dp = prop; dp; dp = dp->next) {
|
||||
if (dp->type == ACPI_DP_TYPE_CHILD) {
|
||||
ret = acpi_dp_write_internal(ctx, dp->child);
|
||||
if (ret)
|
||||
return log_msg_ret("dp child", ret);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int acpi_dp_write(struct acpi_ctx *ctx, struct acpi_dp *table)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = acpi_dp_write_internal(ctx, table);
|
||||
|
||||
/* Clean up */
|
||||
acpi_dp_free(table);
|
||||
|
||||
if (ret)
|
||||
return log_msg_ret("write", ret);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct acpi_dp *acpi_dp_new(struct acpi_dp *dp, enum acpi_dp_type type,
|
||||
const char *name)
|
||||
{
|
||||
struct acpi_dp *new;
|
||||
|
||||
new = malloc(sizeof(struct acpi_dp));
|
||||
if (!new)
|
||||
return NULL;
|
||||
|
||||
memset(new, '\0', sizeof(*new));
|
||||
new->type = type;
|
||||
new->name = name;
|
||||
|
||||
if (dp) {
|
||||
/* Add to end of property list */
|
||||
while (dp->next)
|
||||
dp = dp->next;
|
||||
dp->next = new;
|
||||
}
|
||||
|
||||
return new;
|
||||
}
|
||||
|
||||
struct acpi_dp *acpi_dp_new_table(const char *name)
|
||||
{
|
||||
return acpi_dp_new(NULL, ACPI_DP_TYPE_TABLE, name);
|
||||
}
|
||||
|
||||
struct acpi_dp *acpi_dp_add_integer(struct acpi_dp *dp, const char *name,
|
||||
u64 value)
|
||||
{
|
||||
struct acpi_dp *new;
|
||||
|
||||
assert(dp);
|
||||
new = acpi_dp_new(dp, ACPI_DP_TYPE_INTEGER, name);
|
||||
|
||||
if (new)
|
||||
new->integer = value;
|
||||
|
||||
return new;
|
||||
}
|
||||
|
||||
struct acpi_dp *acpi_dp_add_string(struct acpi_dp *dp, const char *name,
|
||||
const char *string)
|
||||
{
|
||||
struct acpi_dp *new;
|
||||
|
||||
assert(dp);
|
||||
new = acpi_dp_new(dp, ACPI_DP_TYPE_STRING, name);
|
||||
if (new)
|
||||
new->string = string;
|
||||
|
||||
return new;
|
||||
}
|
||||
|
||||
struct acpi_dp *acpi_dp_add_reference(struct acpi_dp *dp, const char *name,
|
||||
const char *reference)
|
||||
{
|
||||
struct acpi_dp *new;
|
||||
|
||||
assert(dp);
|
||||
new = acpi_dp_new(dp, ACPI_DP_TYPE_REFERENCE, name);
|
||||
if (new)
|
||||
new->string = reference;
|
||||
|
||||
return new;
|
||||
}
|
||||
|
||||
struct acpi_dp *acpi_dp_add_child(struct acpi_dp *dp, const char *name,
|
||||
struct acpi_dp *child)
|
||||
{
|
||||
struct acpi_dp *new;
|
||||
|
||||
assert(dp);
|
||||
if (child->type != ACPI_DP_TYPE_TABLE)
|
||||
return NULL;
|
||||
|
||||
new = acpi_dp_new(dp, ACPI_DP_TYPE_CHILD, name);
|
||||
if (new) {
|
||||
new->child = child;
|
||||
new->string = child->name;
|
||||
}
|
||||
|
||||
return new;
|
||||
}
|
||||
|
||||
struct acpi_dp *acpi_dp_add_array(struct acpi_dp *dp, struct acpi_dp *array)
|
||||
{
|
||||
struct acpi_dp *new;
|
||||
|
||||
assert(dp);
|
||||
assert(array);
|
||||
if (array->type != ACPI_DP_TYPE_TABLE)
|
||||
return NULL;
|
||||
|
||||
new = acpi_dp_new(dp, ACPI_DP_TYPE_ARRAY, array->name);
|
||||
if (new)
|
||||
new->array = array;
|
||||
|
||||
return new;
|
||||
}
|
||||
|
||||
struct acpi_dp *acpi_dp_add_integer_array(struct acpi_dp *dp, const char *name,
|
||||
u64 *array, int len)
|
||||
{
|
||||
struct acpi_dp *dp_array;
|
||||
int i;
|
||||
|
||||
assert(dp);
|
||||
if (len <= 0)
|
||||
return NULL;
|
||||
|
||||
dp_array = acpi_dp_new_table(name);
|
||||
if (!dp_array)
|
||||
return NULL;
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
if (!acpi_dp_add_integer(dp_array, NULL, array[i]))
|
||||
break;
|
||||
|
||||
if (!acpi_dp_add_array(dp, dp_array))
|
||||
return NULL;
|
||||
|
||||
return dp_array;
|
||||
}
|
||||
|
||||
struct acpi_dp *acpi_dp_add_gpio(struct acpi_dp *dp, const char *name,
|
||||
const char *ref, int index, int pin,
|
||||
enum acpi_irq_polarity polarity)
|
||||
{
|
||||
struct acpi_dp *gpio;
|
||||
|
||||
assert(dp);
|
||||
gpio = acpi_dp_new_table(name);
|
||||
if (!gpio)
|
||||
return NULL;
|
||||
|
||||
if (!acpi_dp_add_reference(gpio, NULL, ref) ||
|
||||
!acpi_dp_add_integer(gpio, NULL, index) ||
|
||||
!acpi_dp_add_integer(gpio, NULL, pin) ||
|
||||
!acpi_dp_add_integer(gpio, NULL, polarity == ACPI_IRQ_ACTIVE_LOW))
|
||||
return NULL;
|
||||
|
||||
if (!acpi_dp_add_array(dp, gpio))
|
||||
return NULL;
|
||||
|
||||
return gpio;
|
||||
}
|
||||
|
||||
int acpi_dp_ofnode_copy_int(ofnode node, struct acpi_dp *dp, const char *prop)
|
||||
{
|
||||
int ret;
|
||||
u32 val = 0;
|
||||
|
||||
ret = ofnode_read_u32(node, prop, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (!acpi_dp_add_integer(dp, prop, val))
|
||||
return log_ret(-ENOMEM);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int acpi_dp_ofnode_copy_str(ofnode node, struct acpi_dp *dp, const char *prop)
|
||||
{
|
||||
const char *val;
|
||||
|
||||
val = ofnode_read_string(node, prop);
|
||||
if (!val)
|
||||
return -EINVAL;
|
||||
if (!acpi_dp_add_string(dp, prop, val))
|
||||
return log_ret(-ENOMEM);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int acpi_dp_dev_copy_int(const struct udevice *dev, struct acpi_dp *dp,
|
||||
const char *prop)
|
||||
{
|
||||
int ret;
|
||||
u32 val = 0;
|
||||
|
||||
ret = dev_read_u32(dev, prop, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (!acpi_dp_add_integer(dp, prop, val))
|
||||
return log_ret(-ENOMEM);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int acpi_dp_dev_copy_str(const struct udevice *dev, struct acpi_dp *dp,
|
||||
const char *prop)
|
||||
{
|
||||
const char *val;
|
||||
|
||||
val = dev_read_string(dev, prop);
|
||||
if (!val)
|
||||
return -EINVAL;
|
||||
if (!acpi_dp_add_string(dp, prop, val))
|
||||
return log_ret(-ENOMEM);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -237,6 +237,7 @@ static void acpi_write_xsdt(struct acpi_xsdt *xsdt)
|
|||
|
||||
void acpi_setup_base_tables(struct acpi_ctx *ctx, void *start)
|
||||
{
|
||||
ctx->base = start;
|
||||
ctx->current = start;
|
||||
|
||||
/* Align ACPI tables to 16 byte */
|
||||
|
|
616
lib/acpi/acpigen.c
Normal file
616
lib/acpi/acpigen.c
Normal file
|
@ -0,0 +1,616 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Generation of ACPI (Advanced Configuration and Power Interface) tables
|
||||
*
|
||||
* Copyright 2019 Google LLC
|
||||
* Mostly taken from coreboot
|
||||
*/
|
||||
|
||||
#define LOG_CATEGORY LOGC_ACPI
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <log.h>
|
||||
#include <uuid.h>
|
||||
#include <acpi/acpigen.h>
|
||||
#include <acpi/acpi_device.h>
|
||||
#include <acpi/acpi_table.h>
|
||||
#include <dm/acpi.h>
|
||||
|
||||
u8 *acpigen_get_current(struct acpi_ctx *ctx)
|
||||
{
|
||||
return ctx->current;
|
||||
}
|
||||
|
||||
void acpigen_emit_byte(struct acpi_ctx *ctx, uint data)
|
||||
{
|
||||
*(u8 *)ctx->current++ = data;
|
||||
}
|
||||
|
||||
void acpigen_emit_word(struct acpi_ctx *ctx, uint data)
|
||||
{
|
||||
acpigen_emit_byte(ctx, data & 0xff);
|
||||
acpigen_emit_byte(ctx, (data >> 8) & 0xff);
|
||||
}
|
||||
|
||||
void acpigen_emit_dword(struct acpi_ctx *ctx, uint data)
|
||||
{
|
||||
/* Output the value in little-endian format */
|
||||
acpigen_emit_byte(ctx, data & 0xff);
|
||||
acpigen_emit_byte(ctx, (data >> 8) & 0xff);
|
||||
acpigen_emit_byte(ctx, (data >> 16) & 0xff);
|
||||
acpigen_emit_byte(ctx, (data >> 24) & 0xff);
|
||||
}
|
||||
|
||||
/*
|
||||
* Maximum length for an ACPI object generated by this code,
|
||||
*
|
||||
* If you need to change this, change acpigen_write_len_f(ctx) and
|
||||
* acpigen_pop_len(ctx)
|
||||
*/
|
||||
#define ACPIGEN_MAXLEN 0xfffff
|
||||
|
||||
void acpigen_write_len_f(struct acpi_ctx *ctx)
|
||||
{
|
||||
assert(ctx->ltop < (ACPIGEN_LENSTACK_SIZE - 1));
|
||||
ctx->len_stack[ctx->ltop++] = ctx->current;
|
||||
acpigen_emit_byte(ctx, 0);
|
||||
acpigen_emit_byte(ctx, 0);
|
||||
acpigen_emit_byte(ctx, 0);
|
||||
}
|
||||
|
||||
void acpigen_pop_len(struct acpi_ctx *ctx)
|
||||
{
|
||||
int len;
|
||||
char *p;
|
||||
|
||||
assert(ctx->ltop > 0);
|
||||
p = ctx->len_stack[--ctx->ltop];
|
||||
len = ctx->current - (void *)p;
|
||||
assert(len <= ACPIGEN_MAXLEN);
|
||||
/* generate store length for 0xfffff max */
|
||||
p[0] = ACPI_PKG_LEN_3_BYTES | (len & 0xf);
|
||||
p[1] = len >> 4 & 0xff;
|
||||
p[2] = len >> 12 & 0xff;
|
||||
}
|
||||
|
||||
void acpigen_emit_ext_op(struct acpi_ctx *ctx, uint op)
|
||||
{
|
||||
acpigen_emit_byte(ctx, EXT_OP_PREFIX);
|
||||
acpigen_emit_byte(ctx, op);
|
||||
}
|
||||
|
||||
char *acpigen_write_package(struct acpi_ctx *ctx, int nr_el)
|
||||
{
|
||||
char *p;
|
||||
|
||||
acpigen_emit_byte(ctx, PACKAGE_OP);
|
||||
acpigen_write_len_f(ctx);
|
||||
p = ctx->current;
|
||||
acpigen_emit_byte(ctx, nr_el);
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
void acpigen_write_byte(struct acpi_ctx *ctx, unsigned int data)
|
||||
{
|
||||
acpigen_emit_byte(ctx, BYTE_PREFIX);
|
||||
acpigen_emit_byte(ctx, data & 0xff);
|
||||
}
|
||||
|
||||
void acpigen_write_word(struct acpi_ctx *ctx, unsigned int data)
|
||||
{
|
||||
acpigen_emit_byte(ctx, WORD_PREFIX);
|
||||
acpigen_emit_word(ctx, data);
|
||||
}
|
||||
|
||||
void acpigen_write_dword(struct acpi_ctx *ctx, unsigned int data)
|
||||
{
|
||||
acpigen_emit_byte(ctx, DWORD_PREFIX);
|
||||
acpigen_emit_dword(ctx, data);
|
||||
}
|
||||
|
||||
void acpigen_write_qword(struct acpi_ctx *ctx, u64 data)
|
||||
{
|
||||
acpigen_emit_byte(ctx, QWORD_PREFIX);
|
||||
acpigen_emit_dword(ctx, data & 0xffffffff);
|
||||
acpigen_emit_dword(ctx, (data >> 32) & 0xffffffff);
|
||||
}
|
||||
|
||||
void acpigen_write_zero(struct acpi_ctx *ctx)
|
||||
{
|
||||
acpigen_emit_byte(ctx, ZERO_OP);
|
||||
}
|
||||
|
||||
void acpigen_write_one(struct acpi_ctx *ctx)
|
||||
{
|
||||
acpigen_emit_byte(ctx, ONE_OP);
|
||||
}
|
||||
|
||||
void acpigen_write_integer(struct acpi_ctx *ctx, u64 data)
|
||||
{
|
||||
if (data == 0)
|
||||
acpigen_write_zero(ctx);
|
||||
else if (data == 1)
|
||||
acpigen_write_one(ctx);
|
||||
else if (data <= 0xff)
|
||||
acpigen_write_byte(ctx, (unsigned char)data);
|
||||
else if (data <= 0xffff)
|
||||
acpigen_write_word(ctx, (unsigned int)data);
|
||||
else if (data <= 0xffffffff)
|
||||
acpigen_write_dword(ctx, (unsigned int)data);
|
||||
else
|
||||
acpigen_write_qword(ctx, data);
|
||||
}
|
||||
|
||||
void acpigen_write_name_zero(struct acpi_ctx *ctx, const char *name)
|
||||
{
|
||||
acpigen_write_name(ctx, name);
|
||||
acpigen_write_zero(ctx);
|
||||
}
|
||||
|
||||
void acpigen_write_name_one(struct acpi_ctx *ctx, const char *name)
|
||||
{
|
||||
acpigen_write_name(ctx, name);
|
||||
acpigen_write_one(ctx);
|
||||
}
|
||||
|
||||
void acpigen_write_name_byte(struct acpi_ctx *ctx, const char *name, uint val)
|
||||
{
|
||||
acpigen_write_name(ctx, name);
|
||||
acpigen_write_byte(ctx, val);
|
||||
}
|
||||
|
||||
void acpigen_write_name_word(struct acpi_ctx *ctx, const char *name, uint val)
|
||||
{
|
||||
acpigen_write_name(ctx, name);
|
||||
acpigen_write_word(ctx, val);
|
||||
}
|
||||
|
||||
void acpigen_write_name_dword(struct acpi_ctx *ctx, const char *name, uint val)
|
||||
{
|
||||
acpigen_write_name(ctx, name);
|
||||
acpigen_write_dword(ctx, val);
|
||||
}
|
||||
|
||||
void acpigen_write_name_qword(struct acpi_ctx *ctx, const char *name, u64 val)
|
||||
{
|
||||
acpigen_write_name(ctx, name);
|
||||
acpigen_write_qword(ctx, val);
|
||||
}
|
||||
|
||||
void acpigen_write_name_integer(struct acpi_ctx *ctx, const char *name, u64 val)
|
||||
{
|
||||
acpigen_write_name(ctx, name);
|
||||
acpigen_write_integer(ctx, val);
|
||||
}
|
||||
|
||||
void acpigen_write_name_string(struct acpi_ctx *ctx, const char *name,
|
||||
const char *string)
|
||||
{
|
||||
acpigen_write_name(ctx, name);
|
||||
acpigen_write_string(ctx, string);
|
||||
}
|
||||
|
||||
void acpigen_emit_stream(struct acpi_ctx *ctx, const char *data, int size)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < size; i++)
|
||||
acpigen_emit_byte(ctx, data[i]);
|
||||
}
|
||||
|
||||
void acpigen_emit_string(struct acpi_ctx *ctx, const char *str)
|
||||
{
|
||||
acpigen_emit_stream(ctx, str, str ? strlen(str) : 0);
|
||||
acpigen_emit_byte(ctx, '\0');
|
||||
}
|
||||
|
||||
void acpigen_write_string(struct acpi_ctx *ctx, const char *str)
|
||||
{
|
||||
acpigen_emit_byte(ctx, STRING_PREFIX);
|
||||
acpigen_emit_string(ctx, str);
|
||||
}
|
||||
|
||||
/*
|
||||
* The naming conventions for ACPI namespace names are a bit tricky as
|
||||
* each element has to be 4 chars wide ("All names are a fixed 32 bits.")
|
||||
* and "By convention, when an ASL compiler pads a name shorter than 4
|
||||
* characters, it is done so with trailing underscores ('_')".
|
||||
*
|
||||
* Check sections 5.3, 20.2.2 and 20.4 of ACPI spec 6.3 for details.
|
||||
*/
|
||||
static void acpigen_emit_simple_namestring(struct acpi_ctx *ctx,
|
||||
const char *name)
|
||||
{
|
||||
const char *ptr;
|
||||
int i;
|
||||
|
||||
for (i = 0, ptr = name; i < 4; i++) {
|
||||
if (!*ptr || *ptr == '.')
|
||||
acpigen_emit_byte(ctx, '_');
|
||||
else
|
||||
acpigen_emit_byte(ctx, *ptr++);
|
||||
}
|
||||
}
|
||||
|
||||
static void acpigen_emit_double_namestring(struct acpi_ctx *ctx,
|
||||
const char *name, int dotpos)
|
||||
{
|
||||
acpigen_emit_byte(ctx, DUAL_NAME_PREFIX);
|
||||
acpigen_emit_simple_namestring(ctx, name);
|
||||
acpigen_emit_simple_namestring(ctx, &name[dotpos + 1]);
|
||||
}
|
||||
|
||||
static void acpigen_emit_multi_namestring(struct acpi_ctx *ctx,
|
||||
const char *name)
|
||||
{
|
||||
unsigned char *pathlen;
|
||||
int count = 0;
|
||||
|
||||
acpigen_emit_byte(ctx, MULTI_NAME_PREFIX);
|
||||
pathlen = ctx->current;
|
||||
acpigen_emit_byte(ctx, 0);
|
||||
|
||||
while (*name) {
|
||||
acpigen_emit_simple_namestring(ctx, name);
|
||||
/* find end or next entity */
|
||||
while (*name != '.' && *name)
|
||||
name++;
|
||||
/* forward to next */
|
||||
if (*name == '.')
|
||||
name++;
|
||||
count++;
|
||||
}
|
||||
|
||||
*pathlen = count;
|
||||
}
|
||||
|
||||
void acpigen_emit_namestring(struct acpi_ctx *ctx, const char *namepath)
|
||||
{
|
||||
int dotcount;
|
||||
int dotpos;
|
||||
int i;
|
||||
|
||||
/* We can start with a '\' */
|
||||
if (*namepath == '\\') {
|
||||
acpigen_emit_byte(ctx, '\\');
|
||||
namepath++;
|
||||
}
|
||||
|
||||
/* And there can be any number of '^' */
|
||||
while (*namepath == '^') {
|
||||
acpigen_emit_byte(ctx, '^');
|
||||
namepath++;
|
||||
}
|
||||
|
||||
for (i = 0, dotcount = 0; namepath[i]; i++) {
|
||||
if (namepath[i] == '.') {
|
||||
dotcount++;
|
||||
dotpos = i;
|
||||
}
|
||||
}
|
||||
|
||||
/* If we have only \\ or only ^* then we need to add a null name */
|
||||
if (!*namepath)
|
||||
acpigen_emit_byte(ctx, ZERO_OP);
|
||||
else if (dotcount == 0)
|
||||
acpigen_emit_simple_namestring(ctx, namepath);
|
||||
else if (dotcount == 1)
|
||||
acpigen_emit_double_namestring(ctx, namepath, dotpos);
|
||||
else
|
||||
acpigen_emit_multi_namestring(ctx, namepath);
|
||||
}
|
||||
|
||||
void acpigen_write_name(struct acpi_ctx *ctx, const char *namepath)
|
||||
{
|
||||
acpigen_emit_byte(ctx, NAME_OP);
|
||||
acpigen_emit_namestring(ctx, namepath);
|
||||
}
|
||||
|
||||
void acpigen_write_scope(struct acpi_ctx *ctx, const char *scope)
|
||||
{
|
||||
acpigen_emit_byte(ctx, SCOPE_OP);
|
||||
acpigen_write_len_f(ctx);
|
||||
acpigen_emit_namestring(ctx, scope);
|
||||
}
|
||||
|
||||
static void acpigen_write_method_internal(struct acpi_ctx *ctx,
|
||||
const char *name, uint flags)
|
||||
{
|
||||
acpigen_emit_byte(ctx, METHOD_OP);
|
||||
acpigen_write_len_f(ctx);
|
||||
acpigen_emit_namestring(ctx, name);
|
||||
acpigen_emit_byte(ctx, flags);
|
||||
}
|
||||
|
||||
/* Method (name, nargs, NotSerialized) */
|
||||
void acpigen_write_method(struct acpi_ctx *ctx, const char *name, int nargs)
|
||||
{
|
||||
acpigen_write_method_internal(ctx, name,
|
||||
nargs & ACPI_METHOD_NARGS_MASK);
|
||||
}
|
||||
|
||||
/* Method (name, nargs, Serialized) */
|
||||
void acpigen_write_method_serialized(struct acpi_ctx *ctx, const char *name,
|
||||
int nargs)
|
||||
{
|
||||
acpigen_write_method_internal(ctx, name,
|
||||
(nargs & ACPI_METHOD_NARGS_MASK) |
|
||||
ACPI_METHOD_SERIALIZED_MASK);
|
||||
}
|
||||
|
||||
void acpigen_write_device(struct acpi_ctx *ctx, const char *name)
|
||||
{
|
||||
acpigen_emit_ext_op(ctx, DEVICE_OP);
|
||||
acpigen_write_len_f(ctx);
|
||||
acpigen_emit_namestring(ctx, name);
|
||||
}
|
||||
|
||||
void acpigen_write_sta(struct acpi_ctx *ctx, uint status)
|
||||
{
|
||||
/* Method (_STA, 0, NotSerialized) { Return (status) } */
|
||||
acpigen_write_method(ctx, "_STA", 0);
|
||||
acpigen_emit_byte(ctx, RETURN_OP);
|
||||
acpigen_write_byte(ctx, status);
|
||||
acpigen_pop_len(ctx);
|
||||
}
|
||||
|
||||
static void acpigen_write_register(struct acpi_ctx *ctx,
|
||||
const struct acpi_gen_regaddr *addr)
|
||||
{
|
||||
/* See ACPI v6.3 section 6.4.3.7: Generic Register Descriptor */
|
||||
acpigen_emit_byte(ctx, ACPI_DESCRIPTOR_REGISTER);
|
||||
acpigen_emit_byte(ctx, 0x0c); /* Register Length 7:0 */
|
||||
acpigen_emit_byte(ctx, 0x00); /* Register Length 15:8 */
|
||||
acpigen_emit_byte(ctx, addr->space_id);
|
||||
acpigen_emit_byte(ctx, addr->bit_width);
|
||||
acpigen_emit_byte(ctx, addr->bit_offset);
|
||||
acpigen_emit_byte(ctx, addr->access_size);
|
||||
acpigen_emit_dword(ctx, addr->addrl);
|
||||
acpigen_emit_dword(ctx, addr->addrh);
|
||||
}
|
||||
|
||||
void acpigen_write_resourcetemplate_header(struct acpi_ctx *ctx)
|
||||
{
|
||||
/*
|
||||
* A ResourceTemplate() is a Buffer() with a
|
||||
* (Byte|Word|DWord) containing the length, followed by one or more
|
||||
* resource items, terminated by the end tag.
|
||||
* (small item 0xf, len 1)
|
||||
*/
|
||||
acpigen_emit_byte(ctx, BUFFER_OP);
|
||||
acpigen_write_len_f(ctx);
|
||||
acpigen_emit_byte(ctx, WORD_PREFIX);
|
||||
ctx->len_stack[ctx->ltop++] = ctx->current;
|
||||
|
||||
/*
|
||||
* Add two dummy bytes for the ACPI word (keep aligned with the
|
||||
* calculation in acpigen_write_resourcetemplate_footer() below)
|
||||
*/
|
||||
acpigen_emit_byte(ctx, 0x00);
|
||||
acpigen_emit_byte(ctx, 0x00);
|
||||
}
|
||||
|
||||
void acpigen_write_resourcetemplate_footer(struct acpi_ctx *ctx)
|
||||
{
|
||||
char *p = ctx->len_stack[--ctx->ltop];
|
||||
int len;
|
||||
/*
|
||||
* See ACPI v6.3 section 6.4.2.9: End Tag
|
||||
* 0x79 <checksum>
|
||||
* 0x00 is treated as a good checksum according to the spec
|
||||
* and is what iasl generates.
|
||||
*/
|
||||
acpigen_emit_byte(ctx, ACPI_END_TAG);
|
||||
acpigen_emit_byte(ctx, 0x00);
|
||||
|
||||
/*
|
||||
* Start counting past the 2-bytes length added in
|
||||
* acpigen_write_resourcetemplate_header() above
|
||||
*/
|
||||
len = (char *)ctx->current - (p + 2);
|
||||
|
||||
/* patch len word */
|
||||
p[0] = len & 0xff;
|
||||
p[1] = (len >> 8) & 0xff;
|
||||
|
||||
acpigen_pop_len(ctx);
|
||||
}
|
||||
|
||||
void acpigen_write_register_resource(struct acpi_ctx *ctx,
|
||||
const struct acpi_gen_regaddr *addr)
|
||||
{
|
||||
acpigen_write_resourcetemplate_header(ctx);
|
||||
acpigen_write_register(ctx, addr);
|
||||
acpigen_write_resourcetemplate_footer(ctx);
|
||||
}
|
||||
|
||||
/*
|
||||
* ToUUID(uuid)
|
||||
*
|
||||
* ACPI 6.3 Section 19.6.142 table 19-438 defines a special output order for the
|
||||
* bytes that make up a UUID Buffer object:
|
||||
*
|
||||
* UUID byte order for input to this function:
|
||||
* aabbccdd-eeff-gghh-iijj-kkllmmnnoopp
|
||||
*
|
||||
* UUID byte order output by this function:
|
||||
* ddccbbaa-ffee-hhgg-iijj-kkllmmnnoopp
|
||||
*/
|
||||
int acpigen_write_uuid(struct acpi_ctx *ctx, const char *uuid)
|
||||
{
|
||||
u8 buf[UUID_BIN_LEN];
|
||||
int ret;
|
||||
|
||||
/* Parse UUID string into bytes */
|
||||
ret = uuid_str_to_bin(uuid, buf, UUID_STR_FORMAT_GUID);
|
||||
if (ret)
|
||||
return log_msg_ret("bad hex", -EINVAL);
|
||||
|
||||
/* BufferOp */
|
||||
acpigen_emit_byte(ctx, BUFFER_OP);
|
||||
acpigen_write_len_f(ctx);
|
||||
|
||||
/* Buffer length in bytes */
|
||||
acpigen_write_word(ctx, UUID_BIN_LEN);
|
||||
|
||||
/* Output UUID in expected order */
|
||||
acpigen_emit_stream(ctx, (char *)buf, UUID_BIN_LEN);
|
||||
|
||||
acpigen_pop_len(ctx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void acpigen_write_power_res(struct acpi_ctx *ctx, const char *name, uint level,
|
||||
uint order, const char *const dev_states[],
|
||||
size_t dev_states_count)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < dev_states_count; i++) {
|
||||
acpigen_write_name(ctx, dev_states[i]);
|
||||
acpigen_write_package(ctx, 1);
|
||||
acpigen_emit_simple_namestring(ctx, name);
|
||||
acpigen_pop_len(ctx); /* Package */
|
||||
}
|
||||
|
||||
acpigen_emit_ext_op(ctx, POWER_RES_OP);
|
||||
|
||||
acpigen_write_len_f(ctx);
|
||||
|
||||
acpigen_emit_simple_namestring(ctx, name);
|
||||
acpigen_emit_byte(ctx, level);
|
||||
acpigen_emit_word(ctx, order);
|
||||
}
|
||||
|
||||
/* Sleep (ms) */
|
||||
void acpigen_write_sleep(struct acpi_ctx *ctx, u64 sleep_ms)
|
||||
{
|
||||
acpigen_emit_ext_op(ctx, SLEEP_OP);
|
||||
acpigen_write_integer(ctx, sleep_ms);
|
||||
}
|
||||
|
||||
void acpigen_write_store(struct acpi_ctx *ctx)
|
||||
{
|
||||
acpigen_emit_byte(ctx, STORE_OP);
|
||||
}
|
||||
|
||||
/* Or (arg1, arg2, res) */
|
||||
void acpigen_write_or(struct acpi_ctx *ctx, u8 arg1, u8 arg2, u8 res)
|
||||
{
|
||||
acpigen_emit_byte(ctx, OR_OP);
|
||||
acpigen_emit_byte(ctx, arg1);
|
||||
acpigen_emit_byte(ctx, arg2);
|
||||
acpigen_emit_byte(ctx, res);
|
||||
}
|
||||
|
||||
/* And (arg1, arg2, res) */
|
||||
void acpigen_write_and(struct acpi_ctx *ctx, u8 arg1, u8 arg2, u8 res)
|
||||
{
|
||||
acpigen_emit_byte(ctx, AND_OP);
|
||||
acpigen_emit_byte(ctx, arg1);
|
||||
acpigen_emit_byte(ctx, arg2);
|
||||
acpigen_emit_byte(ctx, res);
|
||||
}
|
||||
|
||||
/* Not (arg, res) */
|
||||
void acpigen_write_not(struct acpi_ctx *ctx, u8 arg, u8 res)
|
||||
{
|
||||
acpigen_emit_byte(ctx, NOT_OP);
|
||||
acpigen_emit_byte(ctx, arg);
|
||||
acpigen_emit_byte(ctx, res);
|
||||
}
|
||||
|
||||
/* Store (str, DEBUG) */
|
||||
void acpigen_write_debug_string(struct acpi_ctx *ctx, const char *str)
|
||||
{
|
||||
acpigen_write_store(ctx);
|
||||
acpigen_write_string(ctx, str);
|
||||
acpigen_emit_ext_op(ctx, DEBUG_OP);
|
||||
}
|
||||
|
||||
/**
|
||||
* acpigen_get_dw0_in_local5() - Generate code to put dw0 cfg0 in local5
|
||||
*
|
||||
* Store (\_SB.GPC0 (addr), Local5)
|
||||
*
|
||||
* \_SB.GPC0 is used to read cfg0 value from dw0. It is typically defined in
|
||||
* the board's gpiolib.asl
|
||||
*
|
||||
* The value needs to be stored in a local variable so that it can be used in
|
||||
* expressions in the ACPI code.
|
||||
*
|
||||
* @ctx: ACPI context pointer
|
||||
* @dw0_read: Name to use to read dw0, e.g. "\\_SB.GPC0"
|
||||
* @addr: GPIO pin configuration register address
|
||||
*
|
||||
*/
|
||||
static void acpigen_get_dw0_in_local5(struct acpi_ctx *ctx,
|
||||
const char *dw0_read, ulong addr)
|
||||
{
|
||||
acpigen_write_store(ctx);
|
||||
acpigen_emit_namestring(ctx, dw0_read);
|
||||
acpigen_write_integer(ctx, addr);
|
||||
acpigen_emit_byte(ctx, LOCAL5_OP);
|
||||
}
|
||||
|
||||
/**
|
||||
* acpigen_set_gpio_val() - Emit code to set value of TX GPIO to on/off
|
||||
*
|
||||
* @ctx: ACPI context pointer
|
||||
* @dw0_read: Method name to use to read dw0, e.g. "\\_SB.GPC0"
|
||||
* @dw0_write: Method name to use to read dw0, e.g. "\\_SB.SPC0"
|
||||
* @gpio_num: GPIO number to adjust
|
||||
* @vaL: true to set on, false to set off
|
||||
*/
|
||||
static int acpigen_set_gpio_val(struct acpi_ctx *ctx, u32 tx_state_val,
|
||||
const char *dw0_read, const char *dw0_write,
|
||||
struct acpi_gpio *gpio, bool val)
|
||||
{
|
||||
acpigen_get_dw0_in_local5(ctx, dw0_read, gpio->pin0_addr);
|
||||
|
||||
/* Store (0x40, Local0) */
|
||||
acpigen_write_store(ctx);
|
||||
acpigen_write_integer(ctx, tx_state_val);
|
||||
acpigen_emit_byte(ctx, LOCAL0_OP);
|
||||
|
||||
if (val) {
|
||||
/* Or (Local5, PAD_CFG0_TX_STATE, Local5) */
|
||||
acpigen_write_or(ctx, LOCAL5_OP, LOCAL0_OP, LOCAL5_OP);
|
||||
} else {
|
||||
/* Not (PAD_CFG0_TX_STATE, Local6) */
|
||||
acpigen_write_not(ctx, LOCAL0_OP, LOCAL6_OP);
|
||||
|
||||
/* And (Local5, Local6, Local5) */
|
||||
acpigen_write_and(ctx, LOCAL5_OP, LOCAL6_OP, LOCAL5_OP);
|
||||
}
|
||||
|
||||
/*
|
||||
* \_SB.SPC0 (addr, Local5)
|
||||
* \_SB.SPC0 is used to write cfg0 value in dw0. It is defined in
|
||||
* gpiolib.asl.
|
||||
*/
|
||||
acpigen_emit_namestring(ctx, dw0_write);
|
||||
acpigen_write_integer(ctx, gpio->pin0_addr);
|
||||
acpigen_emit_byte(ctx, LOCAL5_OP);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int acpigen_set_enable_tx_gpio(struct acpi_ctx *ctx, u32 tx_state_val,
|
||||
const char *dw0_read, const char *dw0_write,
|
||||
struct acpi_gpio *gpio, bool enable)
|
||||
{
|
||||
bool set;
|
||||
int ret;
|
||||
|
||||
set = gpio->polarity == ACPI_GPIO_ACTIVE_HIGH ? enable : !enable;
|
||||
ret = acpigen_set_gpio_val(ctx, tx_state_val, dw0_read, dw0_write, gpio,
|
||||
set);
|
||||
if (ret)
|
||||
return log_msg_ret("call", ret);
|
||||
|
||||
return 0;
|
||||
}
|
59
lib/binman.c
59
lib/binman.c
|
@ -11,32 +11,78 @@
|
|||
#include <dm.h>
|
||||
#include <log.h>
|
||||
#include <malloc.h>
|
||||
#include <mapmem.h>
|
||||
|
||||
/**
|
||||
* struct binman_info - Information needed by the binman library
|
||||
*
|
||||
* @image: Node describing the image we are running from
|
||||
* @rom_offset: Offset from an image_pos to the memory-mapped address, or
|
||||
* ROM_OFFSET_NONE if the ROM is not memory-mapped. Can be positive or
|
||||
* negative
|
||||
*/
|
||||
struct binman_info {
|
||||
ofnode image;
|
||||
int rom_offset;
|
||||
};
|
||||
|
||||
#define ROM_OFFSET_NONE (-1)
|
||||
|
||||
static struct binman_info *binman;
|
||||
|
||||
int binman_entry_find(const char *name, struct binman_entry *entry)
|
||||
static int binman_entry_find_internal(ofnode node, const char *name,
|
||||
struct binman_entry *entry)
|
||||
{
|
||||
ofnode node;
|
||||
int ret;
|
||||
|
||||
node = ofnode_find_subnode(binman->image, name);
|
||||
if (!ofnode_valid(node))
|
||||
return log_msg_ret("no binman node", -ENOENT);
|
||||
node = binman->image;
|
||||
node = ofnode_find_subnode(node, name);
|
||||
if (!ofnode_valid(node))
|
||||
return log_msg_ret("node", -ENOENT);
|
||||
|
||||
ret = ofnode_read_u32(node, "image-pos", &entry->image_pos);
|
||||
if (ret)
|
||||
return log_msg_ret("bad binman node1", ret);
|
||||
return log_msg_ret("import-pos", ret);
|
||||
ret = ofnode_read_u32(node, "size", &entry->size);
|
||||
if (ret)
|
||||
return log_msg_ret("bad binman node2", ret);
|
||||
return log_msg_ret("size", ret);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int binman_entry_find(const char *name, struct binman_entry *entry)
|
||||
{
|
||||
return binman_entry_find_internal(binman->image, name, entry);
|
||||
}
|
||||
|
||||
int binman_entry_map(ofnode parent, const char *name, void **bufp, int *sizep)
|
||||
{
|
||||
struct binman_entry entry;
|
||||
int ret;
|
||||
|
||||
if (binman->rom_offset == ROM_OFFSET_NONE)
|
||||
return -EPERM;
|
||||
ret = binman_entry_find_internal(parent, name, &entry);
|
||||
if (ret)
|
||||
return log_msg_ret("entry", ret);
|
||||
if (sizep)
|
||||
*sizep = entry.size;
|
||||
*bufp = map_sysmem(entry.image_pos + binman->rom_offset, entry.size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ofnode binman_section_find_node(const char *name)
|
||||
{
|
||||
return ofnode_find_subnode(binman->image, name);
|
||||
}
|
||||
|
||||
void binman_set_rom_offset(int rom_offset)
|
||||
{
|
||||
binman->rom_offset = rom_offset;
|
||||
}
|
||||
|
||||
int binman_init(void)
|
||||
{
|
||||
binman = malloc(sizeof(struct binman_info));
|
||||
|
@ -45,6 +91,7 @@ int binman_init(void)
|
|||
binman->image = ofnode_path("/binman");
|
||||
if (!ofnode_valid(binman->image))
|
||||
return log_msg_ret("binman node", -EINVAL);
|
||||
binman->rom_offset = ROM_OFFSET_NONE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -152,6 +152,11 @@ uint64_t __weak get_timer_us(uint64_t base)
|
|||
return tick_to_time_us(get_ticks()) - base;
|
||||
}
|
||||
|
||||
unsigned long __weak get_timer_us_long(unsigned long base)
|
||||
{
|
||||
return timer_get_us() - base;
|
||||
}
|
||||
|
||||
unsigned long __weak notrace timer_get_us(void)
|
||||
{
|
||||
return tick_to_time(get_ticks() * 1000);
|
||||
|
|
|
@ -14,6 +14,8 @@ obj-$(CONFIG_UT_DM) += test-uclass.o
|
|||
obj-$(CONFIG_UT_DM) += core.o
|
||||
ifneq ($(CONFIG_SANDBOX),)
|
||||
obj-$(CONFIG_ACPIGEN) += acpi.o
|
||||
obj-$(CONFIG_ACPIGEN) += acpigen.o
|
||||
obj-$(CONFIG_ACPIGEN) += acpi_dp.o
|
||||
obj-$(CONFIG_SOUND) += audio.o
|
||||
obj-$(CONFIG_BLK) += blk.o
|
||||
obj-$(CONFIG_BOARD) += board.o
|
||||
|
|
278
test/dm/acpi.c
278
test/dm/acpi.c
|
@ -14,14 +14,27 @@
|
|||
#include <version.h>
|
||||
#include <tables_csum.h>
|
||||
#include <version.h>
|
||||
#include <acpi/acpigen.h>
|
||||
#include <acpi/acpi_device.h>
|
||||
#include <acpi/acpi_table.h>
|
||||
#include <dm/acpi.h>
|
||||
#include <dm/test.h>
|
||||
#include <test/ut.h>
|
||||
#include "acpi.h"
|
||||
|
||||
#define ACPI_TEST_DEV_NAME "ABCD"
|
||||
#define BUF_SIZE 4096
|
||||
|
||||
/**
|
||||
* struct testacpi_platdata - Platform data for the test ACPI device
|
||||
*
|
||||
* @no_name: true to emit an empty ACPI name from testacpi_get_name()
|
||||
* @return_error: true to return an error instead of a name
|
||||
*/
|
||||
struct testacpi_platdata {
|
||||
bool return_error;
|
||||
bool no_name;
|
||||
};
|
||||
|
||||
static int testacpi_write_tables(const struct udevice *dev,
|
||||
struct acpi_ctx *ctx)
|
||||
{
|
||||
|
@ -40,12 +53,51 @@ static int testacpi_write_tables(const struct udevice *dev,
|
|||
|
||||
static int testacpi_get_name(const struct udevice *dev, char *out_name)
|
||||
{
|
||||
return acpi_copy_name(out_name, ACPI_TEST_DEV_NAME);
|
||||
struct testacpi_platdata *plat = dev_get_platdata(dev);
|
||||
|
||||
if (plat->return_error)
|
||||
return -EINVAL;
|
||||
if (plat->no_name) {
|
||||
*out_name = '\0';
|
||||
return 0;
|
||||
}
|
||||
if (device_get_uclass_id(dev->parent) == UCLASS_TEST_ACPI)
|
||||
return acpi_copy_name(out_name, ACPI_TEST_CHILD_NAME);
|
||||
else
|
||||
return acpi_copy_name(out_name, ACPI_TEST_DEV_NAME);
|
||||
}
|
||||
|
||||
static int testacpi_fill_ssdt(const struct udevice *dev, struct acpi_ctx *ctx)
|
||||
{
|
||||
const char *data;
|
||||
|
||||
data = dev_read_string(dev, "acpi-ssdt-test-data");
|
||||
if (data) {
|
||||
while (*data)
|
||||
acpigen_emit_byte(ctx, *data++);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int testacpi_inject_dsdt(const struct udevice *dev, struct acpi_ctx *ctx)
|
||||
{
|
||||
const char *data;
|
||||
|
||||
data = dev_read_string(dev, "acpi-dsdt-test-data");
|
||||
if (data) {
|
||||
while (*data)
|
||||
acpigen_emit_byte(ctx, *data++);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct acpi_ops testacpi_ops = {
|
||||
.get_name = testacpi_get_name,
|
||||
.write_tables = testacpi_write_tables,
|
||||
.fill_ssdt = testacpi_fill_ssdt,
|
||||
.inject_dsdt = testacpi_inject_dsdt,
|
||||
};
|
||||
|
||||
static const struct udevice_id testacpi_ids[] = {
|
||||
|
@ -57,6 +109,8 @@ U_BOOT_DRIVER(testacpi_drv) = {
|
|||
.name = "testacpi_drv",
|
||||
.of_match = testacpi_ids,
|
||||
.id = UCLASS_TEST_ACPI,
|
||||
.bind = dm_scan_fdt_dev,
|
||||
.platdata_auto_alloc_size = sizeof(struct testacpi_platdata),
|
||||
ACPI_OPS_PTR(&testacpi_ops)
|
||||
};
|
||||
|
||||
|
@ -69,12 +123,52 @@ UCLASS_DRIVER(testacpi) = {
|
|||
static int dm_test_acpi_get_name(struct unit_test_state *uts)
|
||||
{
|
||||
char name[ACPI_NAME_MAX];
|
||||
struct udevice *dev;
|
||||
struct udevice *dev, *dev2, *i2c, *spi, *serial, *timer, *sound;
|
||||
struct udevice *pci, *root;
|
||||
|
||||
/* Test getting the name from the driver */
|
||||
ut_assertok(uclass_first_device_err(UCLASS_TEST_ACPI, &dev));
|
||||
ut_assertok(acpi_get_name(dev, name));
|
||||
ut_asserteq_str(ACPI_TEST_DEV_NAME, name);
|
||||
|
||||
/* Test getting the name from the device tree */
|
||||
ut_assertok(uclass_get_device_by_name(UCLASS_TEST_FDT, "a-test",
|
||||
&dev2));
|
||||
ut_assertok(acpi_get_name(dev2, name));
|
||||
ut_asserteq_str("GHIJ", name);
|
||||
|
||||
/* Test getting the name from acpi_device_get_name() */
|
||||
ut_assertok(uclass_first_device(UCLASS_I2C, &i2c));
|
||||
ut_assertok(acpi_get_name(i2c, name));
|
||||
ut_asserteq_str("I2C0", name);
|
||||
|
||||
ut_assertok(uclass_first_device(UCLASS_SPI, &spi));
|
||||
ut_assertok(acpi_get_name(spi, name));
|
||||
ut_asserteq_str("SPI0", name);
|
||||
|
||||
/* The uart has no sequence number, so this should fail */
|
||||
ut_assertok(uclass_first_device(UCLASS_SERIAL, &serial));
|
||||
ut_asserteq(-ENXIO, acpi_get_name(serial, name));
|
||||
|
||||
/* ACPI doesn't know about the timer */
|
||||
ut_assertok(uclass_first_device(UCLASS_TIMER, &timer));
|
||||
ut_asserteq(-ENOENT, acpi_get_name(timer, name));
|
||||
|
||||
/* May as well test the rest of the cases */
|
||||
ut_assertok(uclass_first_device(UCLASS_SOUND, &sound));
|
||||
ut_assertok(acpi_get_name(sound, name));
|
||||
ut_asserteq_str("HDAS", name);
|
||||
|
||||
ut_assertok(uclass_first_device(UCLASS_PCI, &pci));
|
||||
ut_assertok(acpi_get_name(pci, name));
|
||||
ut_asserteq_str("PCI0", name);
|
||||
|
||||
ut_assertok(uclass_first_device(UCLASS_ROOT, &root));
|
||||
ut_assertok(acpi_get_name(root, name));
|
||||
ut_asserteq_str("\\_SB", name);
|
||||
|
||||
/* Note that we don't have tests for acpi_name_from_id() */
|
||||
|
||||
return 0;
|
||||
}
|
||||
DM_TEST(dm_test_acpi_get_name, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
|
||||
|
@ -138,6 +232,7 @@ static int dm_test_acpi_write_tables(struct unit_test_state *uts)
|
|||
struct acpi_dmar *dmar;
|
||||
struct acpi_ctx ctx;
|
||||
void *buf;
|
||||
int i;
|
||||
|
||||
buf = malloc(BUF_SIZE);
|
||||
ut_assertnonnull(buf);
|
||||
|
@ -147,24 +242,26 @@ static int dm_test_acpi_write_tables(struct unit_test_state *uts)
|
|||
ut_assertok(acpi_write_dev_tables(&ctx));
|
||||
|
||||
/*
|
||||
* We should have two dmar tables, one for each "denx,u-boot-acpi-test"
|
||||
* device
|
||||
* We should have three dmar tables, one for each
|
||||
* "denx,u-boot-acpi-test" device
|
||||
*/
|
||||
ut_asserteq_ptr(dmar + 2, ctx.current);
|
||||
ut_asserteq_ptr(dmar + 3, ctx.current);
|
||||
ut_asserteq(DMAR_INTR_REMAP, dmar->flags);
|
||||
ut_asserteq(32 - 1, dmar->host_address_width);
|
||||
|
||||
ut_asserteq(DMAR_INTR_REMAP, dmar[1].flags);
|
||||
ut_asserteq(32 - 1, dmar[1].host_address_width);
|
||||
|
||||
/* Check that the pointers were added correctly */
|
||||
ut_asserteq(map_to_sysmem(dmar), ctx.rsdt->entry[0]);
|
||||
ut_asserteq(map_to_sysmem(dmar + 1), ctx.rsdt->entry[1]);
|
||||
ut_asserteq(0, ctx.rsdt->entry[2]);
|
||||
ut_asserteq(DMAR_INTR_REMAP, dmar[2].flags);
|
||||
ut_asserteq(32 - 1, dmar[2].host_address_width);
|
||||
|
||||
ut_asserteq(map_to_sysmem(dmar), ctx.xsdt->entry[0]);
|
||||
ut_asserteq(map_to_sysmem(dmar + 1), ctx.xsdt->entry[1]);
|
||||
ut_asserteq(0, ctx.xsdt->entry[2]);
|
||||
/* Check that the pointers were added correctly */
|
||||
for (i = 0; i < 3; i++) {
|
||||
ut_asserteq(map_to_sysmem(dmar + i), ctx.rsdt->entry[i]);
|
||||
ut_asserteq(map_to_sysmem(dmar + i), ctx.xsdt->entry[i]);
|
||||
}
|
||||
ut_asserteq(0, ctx.rsdt->entry[3]);
|
||||
ut_asserteq(0, ctx.xsdt->entry[3]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -268,15 +365,18 @@ static int dm_test_acpi_cmd_list(struct unit_test_state *uts)
|
|||
addr = ALIGN(addr + sizeof(struct acpi_rsdp), 16);
|
||||
ut_assert_nextline("RSDT %08lx %06lx (v01 U-BOOT U-BOOTBL %u INTL 0)",
|
||||
addr, sizeof(struct acpi_table_header) +
|
||||
2 * sizeof(u32), U_BOOT_BUILD_DATE);
|
||||
3 * sizeof(u32), U_BOOT_BUILD_DATE);
|
||||
addr = ALIGN(addr + sizeof(struct acpi_rsdt), 16);
|
||||
ut_assert_nextline("XSDT %08lx %06lx (v01 U-BOOT U-BOOTBL %u INTL 0)",
|
||||
addr, sizeof(struct acpi_table_header) +
|
||||
2 * sizeof(u64), U_BOOT_BUILD_DATE);
|
||||
3 * sizeof(u64), U_BOOT_BUILD_DATE);
|
||||
addr = ALIGN(addr + sizeof(struct acpi_xsdt), 64);
|
||||
ut_assert_nextline("DMAR %08lx %06lx (v01 U-BOOT U-BOOTBL %u INTL 0)",
|
||||
addr, sizeof(struct acpi_dmar), U_BOOT_BUILD_DATE);
|
||||
addr = ALIGN(addr + sizeof(struct acpi_dmar), 16);
|
||||
ut_assert_nextline("DMAR %08lx %06lx (v01 U-BOOT U-BOOTBL %u INTL 0)",
|
||||
addr, sizeof(struct acpi_dmar), U_BOOT_BUILD_DATE);
|
||||
addr = ALIGN(addr + sizeof(struct acpi_dmar), 16);
|
||||
ut_assert_nextline("DMAR %08lx %06lx (v01 U-BOOT U-BOOTBL %u INTL 0)",
|
||||
addr, sizeof(struct acpi_dmar), U_BOOT_BUILD_DATE);
|
||||
ut_assert_console_end();
|
||||
|
@ -315,3 +415,151 @@ static int dm_test_acpi_cmd_dump(struct unit_test_state *uts)
|
|||
return 0;
|
||||
}
|
||||
DM_TEST(dm_test_acpi_cmd_dump, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
|
||||
|
||||
/* Test acpi_device_path() */
|
||||
static int dm_test_acpi_device_path(struct unit_test_state *uts)
|
||||
{
|
||||
struct testacpi_platdata *plat;
|
||||
char buf[ACPI_PATH_MAX];
|
||||
struct udevice *dev, *child;
|
||||
|
||||
ut_assertok(uclass_first_device_err(UCLASS_TEST_ACPI, &dev));
|
||||
ut_assertok(acpi_device_path(dev, buf, sizeof(buf)));
|
||||
ut_asserteq_str("\\_SB." ACPI_TEST_DEV_NAME, buf);
|
||||
|
||||
/* Test running out of space */
|
||||
buf[5] = '\0';
|
||||
ut_asserteq(-ENOSPC, acpi_device_path(dev, buf, 5));
|
||||
ut_asserteq('\0', buf[5]);
|
||||
|
||||
/* Test a three-component name */
|
||||
ut_assertok(device_first_child_err(dev, &child));
|
||||
ut_assertok(acpi_device_path(child, buf, sizeof(buf)));
|
||||
ut_asserteq_str("\\_SB." ACPI_TEST_DEV_NAME "." ACPI_TEST_CHILD_NAME,
|
||||
buf);
|
||||
|
||||
/* Test handling of a device which doesn't produce a name */
|
||||
plat = dev_get_platdata(dev);
|
||||
plat->no_name = true;
|
||||
ut_assertok(acpi_device_path(child, buf, sizeof(buf)));
|
||||
ut_asserteq_str("\\_SB." ACPI_TEST_CHILD_NAME, buf);
|
||||
|
||||
/* Test handling of a device which returns an error */
|
||||
plat = dev_get_platdata(dev);
|
||||
plat->return_error = true;
|
||||
ut_asserteq(-EINVAL, acpi_device_path(child, buf, sizeof(buf)));
|
||||
|
||||
return 0;
|
||||
}
|
||||
DM_TEST(dm_test_acpi_device_path, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
|
||||
|
||||
/* Test acpi_device_status() */
|
||||
static int dm_test_acpi_device_status(struct unit_test_state *uts)
|
||||
{
|
||||
struct udevice *dev;
|
||||
|
||||
ut_assertok(uclass_first_device_err(UCLASS_TEST_ACPI, &dev));
|
||||
ut_asserteq(ACPI_DSTATUS_ALL_ON, acpi_device_status(dev));
|
||||
|
||||
return 0;
|
||||
}
|
||||
DM_TEST(dm_test_acpi_device_status, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
|
||||
|
||||
/* Test acpi_fill_ssdt() */
|
||||
static int dm_test_acpi_fill_ssdt(struct unit_test_state *uts)
|
||||
{
|
||||
struct acpi_ctx ctx;
|
||||
u8 *buf;
|
||||
|
||||
buf = malloc(BUF_SIZE);
|
||||
ut_assertnonnull(buf);
|
||||
|
||||
ctx.current = buf;
|
||||
buf[4] = 'z'; /* sentinel */
|
||||
ut_assertok(acpi_fill_ssdt(&ctx));
|
||||
|
||||
/*
|
||||
* These values come from acpi-test2's acpi-ssdt-test-data property.
|
||||
* This device comes first because of u-boot,acpi-ssdt-order
|
||||
*/
|
||||
ut_asserteq('c', buf[0]);
|
||||
ut_asserteq('d', buf[1]);
|
||||
|
||||
/* These values come from acpi-test's acpi-ssdt-test-data property */
|
||||
ut_asserteq('a', buf[2]);
|
||||
ut_asserteq('b', buf[3]);
|
||||
|
||||
ut_asserteq('z', buf[4]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
DM_TEST(dm_test_acpi_fill_ssdt, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
|
||||
|
||||
/* Test acpi_inject_dsdt() */
|
||||
static int dm_test_acpi_inject_dsdt(struct unit_test_state *uts)
|
||||
{
|
||||
struct acpi_ctx ctx;
|
||||
u8 *buf;
|
||||
|
||||
buf = malloc(BUF_SIZE);
|
||||
ut_assertnonnull(buf);
|
||||
|
||||
ctx.current = buf;
|
||||
buf[4] = 'z'; /* sentinel */
|
||||
ut_assertok(acpi_inject_dsdt(&ctx));
|
||||
|
||||
/*
|
||||
* These values come from acpi-test's acpi-dsdt-test-data property.
|
||||
* There is no u-boot,acpi-dsdt-order so device-tree order is used.
|
||||
*/
|
||||
ut_asserteq('h', buf[0]);
|
||||
ut_asserteq('i', buf[1]);
|
||||
|
||||
/* These values come from acpi-test's acpi-dsdt-test-data property */
|
||||
ut_asserteq('j', buf[2]);
|
||||
ut_asserteq('k', buf[3]);
|
||||
|
||||
ut_asserteq('z', buf[4]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
DM_TEST(dm_test_acpi_inject_dsdt, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
|
||||
|
||||
/* Test 'acpi items' command */
|
||||
static int dm_test_acpi_cmd_items(struct unit_test_state *uts)
|
||||
{
|
||||
struct acpi_ctx ctx;
|
||||
void *buf;
|
||||
|
||||
buf = malloc(BUF_SIZE);
|
||||
ut_assertnonnull(buf);
|
||||
|
||||
ctx.current = buf;
|
||||
ut_assertok(acpi_fill_ssdt(&ctx));
|
||||
console_record_reset();
|
||||
run_command("acpi items", 0);
|
||||
ut_assert_nextline("dev 'acpi-test', type 1, size 2");
|
||||
ut_assert_nextline("dev 'acpi-test2', type 1, size 2");
|
||||
ut_assert_console_end();
|
||||
|
||||
ctx.current = buf;
|
||||
ut_assertok(acpi_inject_dsdt(&ctx));
|
||||
console_record_reset();
|
||||
run_command("acpi items", 0);
|
||||
ut_assert_nextline("dev 'acpi-test', type 2, size 2");
|
||||
ut_assert_nextline("dev 'acpi-test2', type 2, size 2");
|
||||
ut_assert_console_end();
|
||||
|
||||
console_record_reset();
|
||||
run_command("acpi items -d", 0);
|
||||
ut_assert_nextline("dev 'acpi-test', type 2, size 2");
|
||||
ut_assert_nextlines_are_dump(2);
|
||||
ut_assert_nextline("%s", "");
|
||||
ut_assert_nextline("dev 'acpi-test2', type 2, size 2");
|
||||
ut_assert_nextlines_are_dump(2);
|
||||
ut_assert_nextline("%s", "");
|
||||
ut_assert_console_end();
|
||||
|
||||
return 0;
|
||||
}
|
||||
DM_TEST(dm_test_acpi_cmd_items, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
|
||||
|
|
32
test/dm/acpi.h
Normal file
32
test/dm/acpi.h
Normal file
|
@ -0,0 +1,32 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* Common functions for ACPI tests
|
||||
*
|
||||
* Copyright 2020 Google LLC
|
||||
* Written by Simon Glass <sjg@chromium.org>
|
||||
*/
|
||||
|
||||
#ifndef __TEST_DM_ACPI_H
|
||||
#define __TEST_DM_ACPI_H
|
||||
|
||||
#define ACPI_TEST_DEV_NAME "ABCD"
|
||||
#define ACPI_TEST_CHILD_NAME "EFGH"
|
||||
|
||||
/**
|
||||
* acpi_test_alloc_context_size() - Allocate an ACPI context of a given size
|
||||
*
|
||||
* @ctxp: Returns allocated context
|
||||
* @size: Size to allocate in bytes
|
||||
* @return 0 if OK, -ENOMEM if out of memory
|
||||
*/
|
||||
int acpi_test_alloc_context_size(struct acpi_ctx **ctxp, int size);
|
||||
|
||||
/**
|
||||
* acpi_test_get_length() - decode a three-byte length field
|
||||
*
|
||||
* @ptr: Length encoded as per ACPI
|
||||
* @return decoded length, or -EINVAL on error
|
||||
*/
|
||||
int acpi_test_get_length(u8 *ptr);
|
||||
|
||||
#endif /*__TEST_DM_ACPI_H */
|
492
test/dm/acpi_dp.c
Normal file
492
test/dm/acpi_dp.c
Normal file
|
@ -0,0 +1,492 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Tests for ACPI code generation via a device-property table
|
||||
*
|
||||
* Copyright 2019 Google LLC
|
||||
* Written by Simon Glass <sjg@chromium.org>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <uuid.h>
|
||||
#include <acpi/acpigen.h>
|
||||
#include <acpi/acpi_dp.h>
|
||||
#include <asm/unaligned.h>
|
||||
#include <dm/acpi.h>
|
||||
#include <dm/test.h>
|
||||
#include <test/ut.h>
|
||||
#include "acpi.h"
|
||||
|
||||
/* Maximum size of the ACPI context needed for most tests */
|
||||
#define ACPI_CONTEXT_SIZE 500
|
||||
|
||||
#define TEST_INT8 0x7d
|
||||
#define TEST_INT16 0x2345
|
||||
#define TEST_INT32 0x12345678
|
||||
#define TEST_INT64 0x4567890123456
|
||||
#define TEST_STR "testing acpi strings"
|
||||
#define TEST_REF "\\SB.I2C0.TPM2"
|
||||
#define EXPECT_REF "SB__I2C0TPM2"
|
||||
|
||||
static int alloc_context(struct acpi_ctx **ctxp)
|
||||
{
|
||||
return acpi_test_alloc_context_size(ctxp, ACPI_CONTEXT_SIZE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void free_context(struct acpi_ctx **ctxp)
|
||||
{
|
||||
free(*ctxp);
|
||||
*ctxp = NULL;
|
||||
}
|
||||
|
||||
/* Test emitting an empty table */
|
||||
static int dm_test_acpi_dp_new_table(struct unit_test_state *uts)
|
||||
{
|
||||
struct acpi_ctx *ctx;
|
||||
struct acpi_dp *dp;
|
||||
u8 *ptr;
|
||||
|
||||
ut_assertok(alloc_context(&ctx));
|
||||
|
||||
dp = acpi_dp_new_table("FRED");
|
||||
ut_assertnonnull(dp);
|
||||
|
||||
ptr = acpigen_get_current(ctx);
|
||||
ut_assertok(acpi_dp_write(ctx, dp));
|
||||
ut_asserteq(10, acpigen_get_current(ctx) - ptr);
|
||||
ut_asserteq(NAME_OP, *(u8 *)ptr);
|
||||
ut_asserteq_strn("FRED", (char *)ptr + 1);
|
||||
ut_asserteq(PACKAGE_OP, ptr[5]);
|
||||
ut_asserteq(4, acpi_test_get_length(ptr + 6));
|
||||
ut_asserteq(0, ptr[9]);
|
||||
|
||||
free_context(&ctx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
DM_TEST(dm_test_acpi_dp_new_table, 0);
|
||||
|
||||
/* Test emitting an integer */
|
||||
static int dm_test_acpi_dp_int(struct unit_test_state *uts)
|
||||
{
|
||||
struct acpi_ctx *ctx;
|
||||
char uuid[UUID_STR_LEN + 1];
|
||||
struct acpi_dp *dp;
|
||||
u8 *ptr;
|
||||
|
||||
ut_assertok(alloc_context(&ctx));
|
||||
|
||||
dp = acpi_dp_new_table("FRED");
|
||||
ut_assertnonnull(dp);
|
||||
ut_assertnonnull(acpi_dp_add_integer(dp, "MARY", TEST_INT32));
|
||||
|
||||
ptr = acpigen_get_current(ctx);
|
||||
ut_assertok(acpi_dp_write(ctx, dp));
|
||||
ut_asserteq(54, acpigen_get_current(ctx) - ptr);
|
||||
ut_asserteq(NAME_OP, *(u8 *)ptr);
|
||||
ut_asserteq_strn("FRED", (char *)ptr + 1);
|
||||
ut_asserteq(PACKAGE_OP, ptr[5]);
|
||||
ut_asserteq(48, acpi_test_get_length(ptr + 6));
|
||||
ut_asserteq(2, ptr[9]);
|
||||
|
||||
/* UUID */
|
||||
ut_asserteq(BUFFER_OP, ptr[10]);
|
||||
ut_asserteq(22, acpi_test_get_length(ptr + 11));
|
||||
ut_asserteq(WORD_PREFIX, ptr[14]);
|
||||
ut_asserteq(16, get_unaligned((u16 *)(ptr + 15)));
|
||||
uuid_bin_to_str(ptr + 17, uuid, 1);
|
||||
ut_asserteq_str(ACPI_DP_UUID, uuid);
|
||||
|
||||
/* Container package */
|
||||
ut_asserteq(PACKAGE_OP, ptr[33]);
|
||||
ut_asserteq(20, acpi_test_get_length(ptr + 34));
|
||||
ut_asserteq(1, ptr[37]);
|
||||
|
||||
/* Package with name and (integer) value */
|
||||
ut_asserteq(PACKAGE_OP, ptr[38]);
|
||||
ut_asserteq(15, acpi_test_get_length(ptr + 39));
|
||||
ut_asserteq(2, ptr[42]);
|
||||
ut_asserteq(STRING_PREFIX, ptr[43]);
|
||||
ut_asserteq_str("MARY", (char *)ptr + 44);
|
||||
|
||||
ut_asserteq(DWORD_PREFIX, ptr[49]);
|
||||
ut_asserteq(TEST_INT32, get_unaligned((u32 *)(ptr + 50)));
|
||||
|
||||
free_context(&ctx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
DM_TEST(dm_test_acpi_dp_int, 0);
|
||||
|
||||
/* Test emitting a 64-bit integer */
|
||||
static int dm_test_acpi_dp_int64(struct unit_test_state *uts)
|
||||
{
|
||||
struct acpi_ctx *ctx;
|
||||
struct acpi_dp *dp;
|
||||
u8 *ptr;
|
||||
|
||||
ut_assertok(alloc_context(&ctx));
|
||||
|
||||
dp = acpi_dp_new_table("FRED");
|
||||
ut_assertnonnull(dp);
|
||||
ut_assertnonnull(acpi_dp_add_integer(dp, "MARY", TEST_INT64));
|
||||
|
||||
ptr = acpigen_get_current(ctx);
|
||||
ut_assertok(acpi_dp_write(ctx, dp));
|
||||
ut_asserteq(58, acpigen_get_current(ctx) - ptr);
|
||||
|
||||
ut_asserteq(QWORD_PREFIX, ptr[49]);
|
||||
ut_asserteq_64(TEST_INT64, get_unaligned((u64 *)(ptr + 50)));
|
||||
|
||||
free_context(&ctx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
DM_TEST(dm_test_acpi_dp_int64, 0);
|
||||
|
||||
/* Test emitting a 16-bit integer */
|
||||
static int dm_test_acpi_dp_int16(struct unit_test_state *uts)
|
||||
{
|
||||
struct acpi_ctx *ctx;
|
||||
struct acpi_dp *dp;
|
||||
u8 *ptr;
|
||||
|
||||
ut_assertok(alloc_context(&ctx));
|
||||
|
||||
dp = acpi_dp_new_table("FRED");
|
||||
ut_assertnonnull(dp);
|
||||
ut_assertnonnull(acpi_dp_add_integer(dp, "MARY", TEST_INT16));
|
||||
|
||||
ptr = acpigen_get_current(ctx);
|
||||
ut_assertok(acpi_dp_write(ctx, dp));
|
||||
ut_asserteq(52, acpigen_get_current(ctx) - ptr);
|
||||
|
||||
ut_asserteq(WORD_PREFIX, ptr[49]);
|
||||
ut_asserteq(TEST_INT16, get_unaligned((u16 *)(ptr + 50)));
|
||||
|
||||
free_context(&ctx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
DM_TEST(dm_test_acpi_dp_int16, 0);
|
||||
|
||||
/* Test emitting a 8-bit integer */
|
||||
static int dm_test_acpi_dp_int8(struct unit_test_state *uts)
|
||||
{
|
||||
struct acpi_ctx *ctx;
|
||||
struct acpi_dp *dp;
|
||||
u8 *ptr;
|
||||
|
||||
ut_assertok(alloc_context(&ctx));
|
||||
|
||||
dp = acpi_dp_new_table("FRED");
|
||||
ut_assertnonnull(dp);
|
||||
ut_assertnonnull(acpi_dp_add_integer(dp, "MARY", TEST_INT8));
|
||||
|
||||
ptr = acpigen_get_current(ctx);
|
||||
ut_assertok(acpi_dp_write(ctx, dp));
|
||||
ut_asserteq(51, acpigen_get_current(ctx) - ptr);
|
||||
|
||||
ut_asserteq(BYTE_PREFIX, ptr[49]);
|
||||
ut_asserteq(TEST_INT8, ptr[50]);
|
||||
|
||||
free_context(&ctx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
DM_TEST(dm_test_acpi_dp_int8, 0);
|
||||
|
||||
/* Test emitting multiple values */
|
||||
static int dm_test_acpi_dp_multiple(struct unit_test_state *uts)
|
||||
{
|
||||
struct acpi_ctx *ctx;
|
||||
struct acpi_dp *dp;
|
||||
u8 *ptr;
|
||||
|
||||
ut_assertok(alloc_context(&ctx));
|
||||
|
||||
dp = acpi_dp_new_table("FRED");
|
||||
ut_assertnonnull(dp);
|
||||
ut_assertnonnull(acpi_dp_add_integer(dp, "int16", TEST_INT16));
|
||||
ut_assertnonnull(acpi_dp_add_string(dp, "str", TEST_STR));
|
||||
ut_assertnonnull(acpi_dp_add_reference(dp, "ref", TEST_REF));
|
||||
|
||||
ptr = acpigen_get_current(ctx);
|
||||
ut_assertok(acpi_dp_write(ctx, dp));
|
||||
ut_asserteq(110, acpigen_get_current(ctx) - ptr);
|
||||
|
||||
ut_asserteq(WORD_PREFIX, ptr[0x32]);
|
||||
ut_asserteq(TEST_INT16, get_unaligned((u16 *)(ptr + 0x33)));
|
||||
ut_asserteq(STRING_PREFIX, ptr[0x3f]);
|
||||
ut_asserteq_str(TEST_STR, (char *)ptr + 0x40);
|
||||
ut_asserteq(ROOT_PREFIX, ptr[0x5f]);
|
||||
ut_asserteq(MULTI_NAME_PREFIX, ptr[0x60]);
|
||||
ut_asserteq(3, ptr[0x61]);
|
||||
ut_asserteq_strn(EXPECT_REF, (char *)ptr + 0x62);
|
||||
|
||||
free_context(&ctx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
DM_TEST(dm_test_acpi_dp_multiple, 0);
|
||||
|
||||
/* Test emitting an array */
|
||||
static int dm_test_acpi_dp_array(struct unit_test_state *uts)
|
||||
{
|
||||
struct acpi_ctx *ctx;
|
||||
struct acpi_dp *dp;
|
||||
u64 speed[4];
|
||||
u8 *ptr;
|
||||
|
||||
ut_assertok(alloc_context(&ctx));
|
||||
|
||||
dp = acpi_dp_new_table("FRED");
|
||||
ut_assertnonnull(dp);
|
||||
speed[0] = TEST_INT8;
|
||||
speed[1] = TEST_INT16;
|
||||
speed[2] = TEST_INT32;
|
||||
speed[3] = TEST_INT64;
|
||||
ut_assertnonnull(acpi_dp_add_integer_array(dp, "speeds", speed,
|
||||
ARRAY_SIZE(speed)));
|
||||
|
||||
ptr = acpigen_get_current(ctx);
|
||||
ut_assertok(acpi_dp_write(ctx, dp));
|
||||
ut_asserteq(75, acpigen_get_current(ctx) - ptr);
|
||||
|
||||
ut_asserteq(BYTE_PREFIX, ptr[0x38]);
|
||||
ut_asserteq(TEST_INT8, ptr[0x39]);
|
||||
|
||||
ut_asserteq(WORD_PREFIX, ptr[0x3a]);
|
||||
ut_asserteq(TEST_INT16, get_unaligned((u16 *)(ptr + 0x3b)));
|
||||
|
||||
ut_asserteq(DWORD_PREFIX, ptr[0x3d]);
|
||||
ut_asserteq(TEST_INT32, get_unaligned((u32 *)(ptr + 0x3e)));
|
||||
|
||||
ut_asserteq(QWORD_PREFIX, ptr[0x42]);
|
||||
ut_asserteq_64(TEST_INT64, get_unaligned((u64 *)(ptr + 0x43)));
|
||||
|
||||
free_context(&ctx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
DM_TEST(dm_test_acpi_dp_array, 0);
|
||||
|
||||
/* Test emitting a child */
|
||||
static int dm_test_acpi_dp_child(struct unit_test_state *uts)
|
||||
{
|
||||
struct acpi_ctx *ctx;
|
||||
struct acpi_dp *dp, *child1, *child2;
|
||||
char uuid[UUID_STR_LEN + 1];
|
||||
u8 *ptr, *pptr;
|
||||
int i;
|
||||
|
||||
ut_assertok(alloc_context(&ctx));
|
||||
|
||||
child1 = acpi_dp_new_table("child");
|
||||
ut_assertnonnull(child1);
|
||||
ut_assertnonnull(acpi_dp_add_integer(child1, "height", TEST_INT16));
|
||||
|
||||
child2 = acpi_dp_new_table("child");
|
||||
ut_assertnonnull(child2);
|
||||
ut_assertnonnull(acpi_dp_add_integer(child2, "age", TEST_INT8));
|
||||
|
||||
dp = acpi_dp_new_table("FRED");
|
||||
ut_assertnonnull(dp);
|
||||
|
||||
ut_assertnonnull(acpi_dp_add_child(dp, "anna", child1));
|
||||
ut_assertnonnull(acpi_dp_add_child(dp, "john", child2));
|
||||
|
||||
ptr = acpigen_get_current(ctx);
|
||||
ut_assertok(acpi_dp_write(ctx, dp));
|
||||
ut_asserteq(178, acpigen_get_current(ctx) - ptr);
|
||||
|
||||
/* UUID for child extension using Hierarchical Data Extension UUID */
|
||||
ut_asserteq(BUFFER_OP, ptr[10]);
|
||||
ut_asserteq(22, acpi_test_get_length(ptr + 11));
|
||||
ut_asserteq(WORD_PREFIX, ptr[14]);
|
||||
ut_asserteq(16, get_unaligned((u16 *)(ptr + 15)));
|
||||
uuid_bin_to_str(ptr + 17, uuid, 1);
|
||||
ut_asserteq_str(ACPI_DP_CHILD_UUID, uuid);
|
||||
|
||||
/* Package with two children */
|
||||
ut_asserteq(PACKAGE_OP, ptr[0x21]);
|
||||
ut_asserteq(0x28, acpi_test_get_length(ptr + 0x22));
|
||||
ut_asserteq(2, ptr[0x25]);
|
||||
|
||||
/* First we expect the two children as string/value */
|
||||
pptr = ptr + 0x26;
|
||||
for (i = 0; i < 2; i++) {
|
||||
ut_asserteq(PACKAGE_OP, pptr[0]);
|
||||
ut_asserteq(0x11, acpi_test_get_length(pptr + 1));
|
||||
ut_asserteq(2, pptr[4]);
|
||||
ut_asserteq(STRING_PREFIX, pptr[5]);
|
||||
ut_asserteq_str(i ? "john" : "anna", (char *)pptr + 6);
|
||||
ut_asserteq(STRING_PREFIX, pptr[11]);
|
||||
ut_asserteq_str("child", (char *)pptr + 12);
|
||||
pptr += 0x12;
|
||||
}
|
||||
|
||||
/* Write the two children */
|
||||
ut_asserteq(0x4a, pptr - ptr);
|
||||
for (i = 0; i < 2; i++) {
|
||||
const char *prop = i ? "age" : "height";
|
||||
const int datalen = i ? 1 : 2;
|
||||
int len = strlen(prop) + 1;
|
||||
|
||||
ut_asserteq(NAME_OP, pptr[0]);
|
||||
ut_asserteq_strn("chil", (char *)pptr + 1);
|
||||
ut_asserteq(PACKAGE_OP, pptr[5]);
|
||||
ut_asserteq(0x27 + len + datalen, acpi_test_get_length(pptr + 6));
|
||||
ut_asserteq(2, pptr[9]);
|
||||
|
||||
/* UUID */
|
||||
ut_asserteq(BUFFER_OP, pptr[10]);
|
||||
ut_asserteq(22, acpi_test_get_length(pptr + 11));
|
||||
ut_asserteq(WORD_PREFIX, pptr[14]);
|
||||
ut_asserteq(16, get_unaligned((u16 *)(pptr + 15)));
|
||||
uuid_bin_to_str(pptr + 17, uuid, 1);
|
||||
ut_asserteq_str(ACPI_DP_UUID, uuid);
|
||||
pptr += 33;
|
||||
|
||||
/* Containing package */
|
||||
ut_asserteq(i ? 0xa1 : 0x6b, pptr - ptr);
|
||||
ut_asserteq(PACKAGE_OP, pptr[0]);
|
||||
ut_asserteq(0xb + len + datalen, acpi_test_get_length(pptr + 1));
|
||||
ut_asserteq(1, pptr[4]);
|
||||
|
||||
/* Package containing the property-name string and the value */
|
||||
pptr += 5;
|
||||
ut_asserteq(i ? 0xa6 : 0x70, pptr - ptr);
|
||||
ut_asserteq(PACKAGE_OP, pptr[0]);
|
||||
ut_asserteq(6 + len + datalen, acpi_test_get_length(pptr + 1));
|
||||
ut_asserteq(2, pptr[4]);
|
||||
|
||||
ut_asserteq(STRING_PREFIX, pptr[5]);
|
||||
ut_asserteq_str(i ? "age" : "height", (char *)pptr + 6);
|
||||
pptr += 6 + len;
|
||||
if (i) {
|
||||
ut_asserteq(BYTE_PREFIX, pptr[0]);
|
||||
ut_asserteq(TEST_INT8, pptr[1]);
|
||||
} else {
|
||||
ut_asserteq(WORD_PREFIX, pptr[0]);
|
||||
ut_asserteq(TEST_INT16,
|
||||
get_unaligned((u16 *)(pptr + 1)));
|
||||
}
|
||||
pptr += 1 + datalen;
|
||||
}
|
||||
ut_asserteq(178, pptr - ptr);
|
||||
|
||||
free_context(&ctx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
DM_TEST(dm_test_acpi_dp_child, 0);
|
||||
|
||||
/* Test emitting a GPIO */
|
||||
static int dm_test_acpi_dp_gpio(struct unit_test_state *uts)
|
||||
{
|
||||
struct acpi_ctx *ctx;
|
||||
struct acpi_dp *dp;
|
||||
u8 *ptr, *pptr;
|
||||
|
||||
ut_assertok(alloc_context(&ctx));
|
||||
|
||||
dp = acpi_dp_new_table("FRED");
|
||||
ut_assertnonnull(dp);
|
||||
|
||||
/* Try a few different parameters */
|
||||
ut_assertnonnull(acpi_dp_add_gpio(dp, "reset", TEST_REF, 0x23, 0x24,
|
||||
ACPI_IRQ_ACTIVE_HIGH));
|
||||
ut_assertnonnull(acpi_dp_add_gpio(dp, "allow", TEST_REF, 0, 0,
|
||||
ACPI_IRQ_ACTIVE_LOW));
|
||||
|
||||
ptr = acpigen_get_current(ctx);
|
||||
ut_assertok(acpi_dp_write(ctx, dp));
|
||||
ut_asserteq(0x6e, acpigen_get_current(ctx) - ptr);
|
||||
|
||||
pptr = ptr + 0x2c; //0x3a;
|
||||
ut_asserteq_str("reset", (char *)pptr);
|
||||
ut_asserteq_strn(EXPECT_REF, (char *)pptr + 0xe);
|
||||
ut_asserteq(0x23, pptr[0x1b]);
|
||||
ut_asserteq(0x24, pptr[0x1d]);
|
||||
ut_asserteq(ZERO_OP, pptr[0x1e]);
|
||||
|
||||
pptr = ptr + 0x51;
|
||||
ut_asserteq_str("allow", (char *)pptr);
|
||||
ut_asserteq_strn(EXPECT_REF, (char *)pptr + 0xe);
|
||||
ut_asserteq(ZERO_OP, pptr[0x1a]);
|
||||
ut_asserteq(ZERO_OP, pptr[0x1b]);
|
||||
ut_asserteq(ONE_OP, pptr[0x1c]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
DM_TEST(dm_test_acpi_dp_gpio, 0);
|
||||
|
||||
/* Test copying info from the device tree to ACPI tables */
|
||||
static int dm_test_acpi_dp_copy(struct unit_test_state *uts)
|
||||
{
|
||||
struct acpi_ctx *ctx;
|
||||
struct udevice *dev;
|
||||
struct acpi_dp *dp;
|
||||
ofnode node;
|
||||
u8 *ptr;
|
||||
|
||||
ut_assertok(alloc_context(&ctx));
|
||||
|
||||
dp = acpi_dp_new_table("FRED");
|
||||
ut_assertnonnull(dp);
|
||||
|
||||
ut_assertok(uclass_get_device(UCLASS_TEST_FDT, 0, &dev));
|
||||
ut_asserteq_str("a-test", dev->name);
|
||||
|
||||
ut_assertok(acpi_dp_dev_copy_int(dev, dp, "int-value"));
|
||||
ut_asserteq(-EINVAL, acpi_dp_dev_copy_int(dev, dp, "missing-value"));
|
||||
ut_assertok(acpi_dp_dev_copy_int(dev, dp, "uint-value"));
|
||||
|
||||
ut_assertok(acpi_dp_dev_copy_str(dev, dp, "str-value"));
|
||||
ut_asserteq(-EINVAL, acpi_dp_dev_copy_str(dev, dp, "missing-value"));
|
||||
|
||||
node = ofnode_path("/chosen");
|
||||
ut_assert(ofnode_valid(node));
|
||||
ut_assertok(acpi_dp_ofnode_copy_int(node, dp, "int-values"));
|
||||
ut_asserteq(-EINVAL,
|
||||
acpi_dp_ofnode_copy_int(node, dp, "missing-value"));
|
||||
|
||||
ut_assertok(acpi_dp_ofnode_copy_str(node, dp, "setting"));
|
||||
ut_asserteq(-EINVAL,
|
||||
acpi_dp_ofnode_copy_str(node, dp, "missing-value"));
|
||||
|
||||
ptr = acpigen_get_current(ctx);
|
||||
ut_assertok(acpi_dp_write(ctx, dp));
|
||||
ut_asserteq(0x9d, acpigen_get_current(ctx) - ptr);
|
||||
|
||||
ut_asserteq(STRING_PREFIX, ptr[0x2b]);
|
||||
ut_asserteq_str("int-value", (char *)ptr + 0x2c);
|
||||
ut_asserteq(WORD_PREFIX, ptr[0x36]);
|
||||
ut_asserteq(1234, get_unaligned((u16 *)(ptr + 0x37)));
|
||||
|
||||
ut_asserteq(STRING_PREFIX, ptr[0x3e]);
|
||||
ut_asserteq_str("uint-value", (char *)ptr + 0x3f);
|
||||
ut_asserteq(DWORD_PREFIX, ptr[0x4a]);
|
||||
ut_asserteq(-1234, get_unaligned((u32 *)(ptr + 0x4b)));
|
||||
|
||||
ut_asserteq(STRING_PREFIX, ptr[0x54]);
|
||||
ut_asserteq_str("str-value", (char *)ptr + 0x55);
|
||||
ut_asserteq(STRING_PREFIX, ptr[0x5f]);
|
||||
ut_asserteq_str("test string", (char *)ptr + 0x60);
|
||||
|
||||
ut_asserteq(STRING_PREFIX, ptr[0x71]);
|
||||
ut_asserteq_str("int-values", (char *)ptr + 0x72);
|
||||
ut_asserteq(WORD_PREFIX, ptr[0x7d]);
|
||||
ut_asserteq(0x1937, get_unaligned((u16 *)(ptr + 0x7e)));
|
||||
|
||||
ut_asserteq(STRING_PREFIX, ptr[0x85]);
|
||||
ut_asserteq_str("setting", (char *)ptr + 0x86);
|
||||
ut_asserteq(STRING_PREFIX, ptr[0x8e]);
|
||||
ut_asserteq_str("sunrise ohoka", (char *)(ptr + 0x8f));
|
||||
|
||||
return 0;
|
||||
}
|
||||
DM_TEST(dm_test_acpi_dp_copy, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue