- 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:
Tom Rini 2020-07-17 08:04:48 -04:00
commit 7c3cc6f106
107 changed files with 7997 additions and 255 deletions

View File

@ -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

View File

@ -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>;

View File

@ -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

View File

@ -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[] = {

View File

@ -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;
}

View File

@ -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;
}
/*

View File

@ -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() */

View File

@ -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

View File

@ -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;

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -24,7 +24,7 @@ int print_cpuinfo(void)
return default_print_cpuinfo();
}
void board_final_cleanup(void)
void board_final_init(void)
{
}

View File

@ -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;
}

View File

@ -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]);
}

View File

@ -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,
};

View File

@ -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;
}

View File

@ -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;

View File

@ -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()

View File

@ -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;
}

View File

@ -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

View 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

View File

@ -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);
/**

View File

@ -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);

View File

@ -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

View File

@ -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);

View File

@ -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)

View File

@ -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);

View File

@ -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
View 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;
}

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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;
}

View File

@ -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,

View File

@ -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));

View File

@ -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)

View File

@ -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 */

View File

@ -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");
}

View File

@ -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];

View File

@ -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));

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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";
};
};

View File

@ -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

View 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 = <&reg_audio>;
VDDMIC-supply = <&reg_audio>;
VDDIO-supply = <&reg_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>;
};
};

View 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>;
};

View File

@ -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

View File

@ -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;
}

View File

@ -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 */

View File

@ -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;

View File

@ -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[] = {

View File

@ -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)
};

View File

@ -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

View File

@ -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_ */

View File

@ -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[] = {

View File

@ -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;

View File

@ -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>

View File

@ -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

View File

@ -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",

View File

@ -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[] = {

View File

@ -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);
}

View File

@ -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[] = {

View File

@ -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

View File

@ -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;
}

View File

@ -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,

View File

@ -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;

View File

@ -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)
};

View File

@ -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

View File

@ -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
View 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
View 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)
};

View File

@ -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
View 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
View 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
View 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

View File

@ -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_ */

View File

@ -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
*

View File

@ -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

View File

@ -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;
}
/**

View 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

View 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 */

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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); \

View File

@ -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()
*

View File

@ -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
View 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
View 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;
}

View File

@ -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
View 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;
}

View File

@ -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;
}

View File

@ -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);

View File

@ -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

View File

@ -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
View 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
View 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