u-boot-brain/arch/x86/cpu/apollolake/hostbridge.c
Simon Glass caa4daa2ae dm: treewide: Rename 'platdata' variables to just 'plat'
We use 'priv' for private data but often use 'platdata' for platform data.
We can't really use 'pdata' since that is ambiguous (it could mean private
or platform data).

Rename some of the latter variables to end with 'plat' for consistency.

Signed-off-by: Simon Glass <sjg@chromium.org>
2020-12-13 16:51:08 -07:00

410 lines
10 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright 2019 Google LLC
* Copyright (C) 2015 - 2017 Intel Corp.
* Copyright (C) 2017 - 2019 Siemens AG
* (Written by Alexandru Gagniuc <alexandrux.gagniuc@intel.com> for Intel Corp.)
* (Written by Andrey Petrov <andrey.petrov@intel.com> for Intel Corp.)
*
* Portions from coreboot soc/intel/apollolake/chip.c
*/
#define LOG_CATEGORY UCLASS_NORTHBRIDGE
#include <common.h>
#include <dm.h>
#include <dt-structs.h>
#include <log.h>
#include <spl.h>
#include <tables_csum.h>
#include <acpi/acpi_table.h>
#include <asm/acpi_nhlt.h>
#include <asm/intel_pinctrl.h>
#include <asm/intel_regs.h>
#include <asm/io.h>
#include <asm/pci.h>
#include <asm/arch/acpi.h>
#include <asm/arch/systemagent.h>
#include <dt-bindings/sound/nhlt.h>
#include <dm/acpi.h>
enum {
PCIEXBAR = 0x60,
PCIEXBAR_LENGTH_256MB = 0,
PCIEXBAR_LENGTH_128MB,
PCIEXBAR_LENGTH_64MB,
PCIEXBAR_PCIEXBAREN = 1 << 0,
BGSM = 0xb4, /* Base GTT Stolen Memory */
TSEG = 0xb8, /* TSEG base */
TOLUD = 0xbc,
};
/**
* struct apl_hostbridge_platdata - platform data for hostbridge
*
* @dtplat: Platform data for of-platdata
* @early_pads: Early pad data to set up, each (pad, cfg0, cfg1)
* @early_pads_count: Number of pads to process
* @pciex_region_size: BAR length in bytes
* @bdf: Bus/device/function of hostbridge
*/
struct apl_hostbridge_platdata {
#if CONFIG_IS_ENABLED(OF_PLATDATA)
struct dtd_intel_apl_hostbridge dtplat;
#endif
u32 *early_pads;
int early_pads_count;
uint pciex_region_size;
pci_dev_t bdf;
};
static const struct nhlt_format_config dmic_1ch_formats[] = {
/* 48 KHz 16-bits per sample. */
{
.num_channels = 1,
.sample_freq_khz = 48,
.container_bits_per_sample = 16,
.valid_bits_per_sample = 16,
.settings_file = "dmic-1ch-48khz-16b.dat",
},
};
static const struct nhlt_dmic_array_config dmic_1ch_mic_config = {
.tdm_config = {
.config_type = NHLT_TDM_MIC_ARRAY,
},
.array_type = NHLT_MIC_ARRAY_VENDOR_DEFINED,
};
static const struct nhlt_endp_descriptor dmic_1ch_descriptors[] = {
{
.link = NHLT_LINK_PDM,
.device = NHLT_PDM_DEV,
.direction = NHLT_DIR_CAPTURE,
.vid = NHLT_VID,
.did = NHLT_DID_DMIC,
.cfg = &dmic_1ch_mic_config,
.cfg_size = sizeof(dmic_1ch_mic_config),
.formats = dmic_1ch_formats,
.num_formats = ARRAY_SIZE(dmic_1ch_formats),
},
};
static const struct nhlt_format_config dmic_2ch_formats[] = {
/* 48 KHz 16-bits per sample. */
{
.num_channels = 2,
.sample_freq_khz = 48,
.container_bits_per_sample = 16,
.valid_bits_per_sample = 16,
.settings_file = "dmic-2ch-48khz-16b.dat",
},
};
static const struct nhlt_dmic_array_config dmic_2ch_mic_config = {
.tdm_config = {
.config_type = NHLT_TDM_MIC_ARRAY,
},
.array_type = NHLT_MIC_ARRAY_2CH_SMALL,
};
static const struct nhlt_endp_descriptor dmic_2ch_descriptors[] = {
{
.link = NHLT_LINK_PDM,
.device = NHLT_PDM_DEV,
.direction = NHLT_DIR_CAPTURE,
.vid = NHLT_VID,
.did = NHLT_DID_DMIC,
.cfg = &dmic_2ch_mic_config,
.cfg_size = sizeof(dmic_2ch_mic_config),
.formats = dmic_2ch_formats,
.num_formats = ARRAY_SIZE(dmic_2ch_formats),
},
};
static const struct nhlt_format_config dmic_4ch_formats[] = {
/* 48 KHz 16-bits per sample. */
{
.num_channels = 4,
.sample_freq_khz = 48,
.container_bits_per_sample = 16,
.valid_bits_per_sample = 16,
.settings_file = "dmic-4ch-48khz-16b.dat",
},
};
static const struct nhlt_dmic_array_config dmic_4ch_mic_config = {
.tdm_config = {
.config_type = NHLT_TDM_MIC_ARRAY,
},
.array_type = NHLT_MIC_ARRAY_4CH_L_SHAPED,
};
static const struct nhlt_endp_descriptor dmic_4ch_descriptors[] = {
{
.link = NHLT_LINK_PDM,
.device = NHLT_PDM_DEV,
.direction = NHLT_DIR_CAPTURE,
.vid = NHLT_VID,
.did = NHLT_DID_DMIC,
.cfg = &dmic_4ch_mic_config,
.cfg_size = sizeof(dmic_4ch_mic_config),
.formats = dmic_4ch_formats,
.num_formats = ARRAY_SIZE(dmic_4ch_formats),
},
};
static int apl_hostbridge_early_init_pinctrl(struct udevice *dev)
{
struct apl_hostbridge_platdata *plat = dev_get_platdata(dev);
struct udevice *pinctrl;
int ret;
ret = uclass_first_device_err(UCLASS_PINCTRL, &pinctrl);
if (ret)
return log_msg_ret("no hostbridge pinctrl", ret);
return pinctrl_config_pads(pinctrl, plat->early_pads,
plat->early_pads_count);
}
static int apl_hostbridge_early_init(struct udevice *dev)
{
struct apl_hostbridge_platdata *plat = dev_get_platdata(dev);
u32 region_size;
ulong base;
u32 reg;
int ret;
/* Set up the MCHBAR */
pci_x86_read_config(plat->bdf, MCHBAR, &base, PCI_SIZE_32);
base = MCH_BASE_ADDRESS;
pci_x86_write_config(plat->bdf, MCHBAR, base | 1, PCI_SIZE_32);
/*
* The PCIEXBAR is assumed to live in the memory mapped IO space under
* 4GiB
*/
pci_x86_write_config(plat->bdf, PCIEXBAR + 4, 0, PCI_SIZE_32);
switch (plat->pciex_region_size >> 20) {
default:
case 256:
region_size = PCIEXBAR_LENGTH_256MB;
break;
case 128:
region_size = PCIEXBAR_LENGTH_128MB;
break;
case 64:
region_size = PCIEXBAR_LENGTH_64MB;
break;
}
reg = CONFIG_MMCONF_BASE_ADDRESS | (region_size << 1)
| PCIEXBAR_PCIEXBAREN;
pci_x86_write_config(plat->bdf, PCIEXBAR, reg, PCI_SIZE_32);
/*
* TSEG defines the base of SMM range. BIOS determines the base
* of TSEG memory which must be at or below Graphics base of GTT
* Stolen memory, hence its better to clear TSEG register early
* to avoid power on default non-zero value (if any).
*/
pci_x86_write_config(plat->bdf, TSEG, 0, PCI_SIZE_32);
ret = apl_hostbridge_early_init_pinctrl(dev);
if (ret)
return log_msg_ret("pinctrl", ret);
return 0;
}
static int apl_hostbridge_ofdata_to_platdata(struct udevice *dev)
{
struct apl_hostbridge_platdata *plat = dev_get_platdata(dev);
struct udevice *pinctrl;
int ret;
/*
* The host bridge holds the early pad data needed to get through TPL.
* This is a small amount of data, enough to fit in TPL, so we keep it
* separate from the full pad data, stored in the fsp-s subnode. That
* subnode is not present in TPL, to save space.
*/
ret = uclass_first_device_err(UCLASS_PINCTRL, &pinctrl);
if (ret)
return log_msg_ret("no hostbridge PINCTRL", ret);
#if !CONFIG_IS_ENABLED(OF_PLATDATA)
int root;
/* Get length of PCI Express Region */
plat->pciex_region_size = dev_read_u32_default(dev, "pciex-region-size",
256 << 20);
root = pci_get_devfn(dev);
if (root < 0)
return log_msg_ret("Cannot get host-bridge PCI address", root);
plat->bdf = root;
ret = pinctrl_read_pads(pinctrl, dev_ofnode(dev), "early-pads",
&plat->early_pads, &plat->early_pads_count);
if (ret)
return log_msg_ret("early-pads", ret);
#else
struct dtd_intel_apl_hostbridge *dtplat = &plat->dtplat;
int size;
plat->pciex_region_size = dtplat->pciex_region_size;
plat->bdf = pci_ofplat_get_devfn(dtplat->reg[0]);
/* Assume that if everything is 0, it is empty */
plat->early_pads = dtplat->early_pads;
size = ARRAY_SIZE(dtplat->early_pads);
plat->early_pads_count = pinctrl_count_pads(pinctrl, plat->early_pads,
size);
#endif
return 0;
}
static int apl_hostbridge_probe(struct udevice *dev)
{
if (spl_phase() == PHASE_TPL)
return apl_hostbridge_early_init(dev);
return 0;
}
static int apl_acpi_hb_get_name(const struct udevice *dev, char *out_name)
{
return acpi_copy_name(out_name, "RHUB");
}
#ifdef CONFIG_GENERATE_ACPI_TABLE
static int apl_acpi_hb_write_tables(const struct udevice *dev,
struct acpi_ctx *ctx)
{
struct acpi_table_header *header;
struct acpi_dmar *dmar;
u32 val;
/*
* Create DMAR table only if virtualization is enabled. Due to some
* constraints on Apollo Lake SoC (some stepping affected), VTD could
* not be enabled together with IPU. Doing so will override and disable
* VTD while leaving CAPID0_A still reporting that VTD is available.
* As in this case FSP will lock VTD to disabled state, we need to make
* sure that DMAR table generation only happens when at least DEFVTBAR
* is enabled. Otherwise the DMAR header will be generated while the
* content of the table will be missing.
*/
dm_pci_read_config32(dev, CAPID0_A, &val);
if ((val & VTD_DISABLE) ||
!(readl(MCHBAR_REG(DEFVTBAR)) & VTBAR_ENABLED))
return 0;
log_debug("ACPI: * DMAR\n");
dmar = (struct acpi_dmar *)ctx->current;
header = &dmar->header;
acpi_create_dmar(dmar, DMAR_INTR_REMAP);
ctx->current += sizeof(struct acpi_dmar);
apl_acpi_fill_dmar(ctx);
/* (Re)calculate length and checksum */
header->length = ctx->current - (void *)dmar;
header->checksum = table_compute_checksum((void *)dmar, header->length);
acpi_align(ctx);
acpi_add_table(ctx, dmar);
return 0;
}
#endif
static int apl_acpi_setup_nhlt(const struct udevice *dev, struct acpi_ctx *ctx)
{
struct nhlt *nhlt = ctx->nhlt;
u32 channels;
ofnode node;
node = ofnode_find_subnode(dev_ofnode(dev), "nhlt");
if (ofnode_read_u32(node, "intel,dmic-channels", &channels))
return log_msg_ret("channels", -EINVAL);
switch (channels) {
case 1:
return nhlt_add_endpoints(nhlt, dmic_1ch_descriptors,
ARRAY_SIZE(dmic_1ch_descriptors));
case 2:
return nhlt_add_endpoints(nhlt, dmic_2ch_descriptors,
ARRAY_SIZE(dmic_2ch_descriptors));
case 4:
return nhlt_add_endpoints(nhlt, dmic_4ch_descriptors,
ARRAY_SIZE(dmic_4ch_descriptors));
}
return log_msg_ret("channels", -EINVAL);
}
static int apl_hostbridge_remove(struct udevice *dev)
{
/*
* TODO(sjg@chromium.org): Consider adding code from coreboot's
* platform_fsp_notify_status()
*/
return 0;
}
static ulong sa_read_reg(struct udevice *dev, int reg)
{
u32 val;
/* All regions concerned for have 1 MiB alignment */
dm_pci_read_config32(dev, BGSM, &val);
return ALIGN_DOWN(val, 1 << 20);
}
ulong sa_get_tolud_base(struct udevice *dev)
{
return sa_read_reg(dev, TOLUD);
}
ulong sa_get_gsm_base(struct udevice *dev)
{
return sa_read_reg(dev, BGSM);
}
ulong sa_get_tseg_base(struct udevice *dev)
{
return sa_read_reg(dev, TSEG);
}
struct acpi_ops apl_hostbridge_acpi_ops = {
.get_name = apl_acpi_hb_get_name,
#ifdef CONFIG_GENERATE_ACPI_TABLE
.write_tables = apl_acpi_hb_write_tables,
#endif
.setup_nhlt = apl_acpi_setup_nhlt,
};
static const struct udevice_id apl_hostbridge_ids[] = {
{ .compatible = "intel,apl-hostbridge" },
{ }
};
U_BOOT_DRIVER(intel_apl_hostbridge) = {
.name = "intel_apl_hostbridge",
.id = UCLASS_NORTHBRIDGE,
.of_match = apl_hostbridge_ids,
.ofdata_to_platdata = apl_hostbridge_ofdata_to_platdata,
.probe = apl_hostbridge_probe,
.remove = apl_hostbridge_remove,
.plat_auto = sizeof(struct apl_hostbridge_platdata),
ACPI_OPS_PTR(&apl_hostbridge_acpi_ops)
.flags = DM_FLAG_OS_PREPARE,
};