uboot/arch/x86/cpu/apollolake/hostbridge.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Copyright 2019 Google LLC
   4 * Copyright (C) 2015 - 2017 Intel Corp.
   5 * Copyright (C) 2017 - 2019 Siemens AG
   6 * (Written by Alexandru Gagniuc <alexandrux.gagniuc@intel.com> for Intel Corp.)
   7 * (Written by Andrey Petrov <andrey.petrov@intel.com> for Intel Corp.)
   8 *
   9 * Portions from coreboot soc/intel/apollolake/chip.c
  10 */
  11
  12#define LOG_CATEGORY UCLASS_NORTHBRIDGE
  13
  14#include <common.h>
  15#include <dm.h>
  16#include <dt-structs.h>
  17#include <log.h>
  18#include <spl.h>
  19#include <tables_csum.h>
  20#include <acpi/acpi_table.h>
  21#include <asm/acpi_nhlt.h>
  22#include <asm/intel_pinctrl.h>
  23#include <asm/intel_regs.h>
  24#include <asm/io.h>
  25#include <asm/pci.h>
  26#include <asm/arch/acpi.h>
  27#include <asm/arch/systemagent.h>
  28#include <dt-bindings/sound/nhlt.h>
  29#include <dm/acpi.h>
  30
  31enum {
  32        PCIEXBAR                = 0x60,
  33        PCIEXBAR_LENGTH_256MB   = 0,
  34        PCIEXBAR_LENGTH_128MB,
  35        PCIEXBAR_LENGTH_64MB,
  36
  37        PCIEXBAR_PCIEXBAREN     = 1 << 0,
  38
  39        BGSM                    = 0xb4,  /* Base GTT Stolen Memory */
  40        TSEG                    = 0xb8,  /* TSEG base */
  41        TOLUD                   = 0xbc,
  42};
  43
  44/**
  45 * struct apl_hostbridge_platdata - platform data for hostbridge
  46 *
  47 * @dtplat: Platform data for of-platdata
  48 * @early_pads: Early pad data to set up, each (pad, cfg0, cfg1)
  49 * @early_pads_count: Number of pads to process
  50 * @pciex_region_size: BAR length in bytes
  51 * @bdf: Bus/device/function of hostbridge
  52 */
  53struct apl_hostbridge_platdata {
  54#if CONFIG_IS_ENABLED(OF_PLATDATA)
  55        struct dtd_intel_apl_hostbridge dtplat;
  56#endif
  57        u32 *early_pads;
  58        int early_pads_count;
  59        uint pciex_region_size;
  60        pci_dev_t bdf;
  61};
  62
  63static const struct nhlt_format_config dmic_1ch_formats[] = {
  64        /* 48 KHz 16-bits per sample. */
  65        {
  66                .num_channels = 1,
  67                .sample_freq_khz = 48,
  68                .container_bits_per_sample = 16,
  69                .valid_bits_per_sample = 16,
  70                .settings_file = "dmic-1ch-48khz-16b.dat",
  71        },
  72};
  73
  74static const struct nhlt_dmic_array_config dmic_1ch_mic_config = {
  75        .tdm_config = {
  76                .config_type = NHLT_TDM_MIC_ARRAY,
  77        },
  78        .array_type = NHLT_MIC_ARRAY_VENDOR_DEFINED,
  79};
  80
  81static const struct nhlt_endp_descriptor dmic_1ch_descriptors[] = {
  82        {
  83                .link = NHLT_LINK_PDM,
  84                .device = NHLT_PDM_DEV,
  85                .direction = NHLT_DIR_CAPTURE,
  86                .vid = NHLT_VID,
  87                .did = NHLT_DID_DMIC,
  88                .cfg = &dmic_1ch_mic_config,
  89                .cfg_size = sizeof(dmic_1ch_mic_config),
  90                .formats = dmic_1ch_formats,
  91                .num_formats = ARRAY_SIZE(dmic_1ch_formats),
  92        },
  93};
  94
  95static const struct nhlt_format_config dmic_2ch_formats[] = {
  96        /* 48 KHz 16-bits per sample. */
  97        {
  98                .num_channels = 2,
  99                .sample_freq_khz = 48,
 100                .container_bits_per_sample = 16,
 101                .valid_bits_per_sample = 16,
 102                .settings_file = "dmic-2ch-48khz-16b.dat",
 103        },
 104};
 105
 106static const struct nhlt_dmic_array_config dmic_2ch_mic_config = {
 107        .tdm_config = {
 108                .config_type = NHLT_TDM_MIC_ARRAY,
 109        },
 110        .array_type = NHLT_MIC_ARRAY_2CH_SMALL,
 111};
 112
 113static const struct nhlt_endp_descriptor dmic_2ch_descriptors[] = {
 114        {
 115                .link = NHLT_LINK_PDM,
 116                .device = NHLT_PDM_DEV,
 117                .direction = NHLT_DIR_CAPTURE,
 118                .vid = NHLT_VID,
 119                .did = NHLT_DID_DMIC,
 120                .cfg = &dmic_2ch_mic_config,
 121                .cfg_size = sizeof(dmic_2ch_mic_config),
 122                .formats = dmic_2ch_formats,
 123                .num_formats = ARRAY_SIZE(dmic_2ch_formats),
 124        },
 125};
 126
 127static const struct nhlt_format_config dmic_4ch_formats[] = {
 128        /* 48 KHz 16-bits per sample. */
 129        {
 130                .num_channels = 4,
 131                .sample_freq_khz = 48,
 132                .container_bits_per_sample = 16,
 133                .valid_bits_per_sample = 16,
 134                .settings_file = "dmic-4ch-48khz-16b.dat",
 135        },
 136};
 137
 138static const struct nhlt_dmic_array_config dmic_4ch_mic_config = {
 139        .tdm_config = {
 140                .config_type = NHLT_TDM_MIC_ARRAY,
 141        },
 142        .array_type = NHLT_MIC_ARRAY_4CH_L_SHAPED,
 143};
 144
 145static const struct nhlt_endp_descriptor dmic_4ch_descriptors[] = {
 146        {
 147                .link = NHLT_LINK_PDM,
 148                .device = NHLT_PDM_DEV,
 149                .direction = NHLT_DIR_CAPTURE,
 150                .vid = NHLT_VID,
 151                .did = NHLT_DID_DMIC,
 152                .cfg = &dmic_4ch_mic_config,
 153                .cfg_size = sizeof(dmic_4ch_mic_config),
 154                .formats = dmic_4ch_formats,
 155                .num_formats = ARRAY_SIZE(dmic_4ch_formats),
 156        },
 157};
 158
 159static int apl_hostbridge_early_init_pinctrl(struct udevice *dev)
 160{
 161        struct apl_hostbridge_platdata *plat = dev_get_platdata(dev);
 162        struct udevice *pinctrl;
 163        int ret;
 164
 165        ret = uclass_first_device_err(UCLASS_PINCTRL, &pinctrl);
 166        if (ret)
 167                return log_msg_ret("no hostbridge pinctrl", ret);
 168
 169        return pinctrl_config_pads(pinctrl, plat->early_pads,
 170                                   plat->early_pads_count);
 171}
 172
 173static int apl_hostbridge_early_init(struct udevice *dev)
 174{
 175        struct apl_hostbridge_platdata *plat = dev_get_platdata(dev);
 176        u32 region_size;
 177        ulong base;
 178        u32 reg;
 179        int ret;
 180
 181        /* Set up the MCHBAR */
 182        pci_x86_read_config(plat->bdf, MCHBAR, &base, PCI_SIZE_32);
 183        base = MCH_BASE_ADDRESS;
 184        pci_x86_write_config(plat->bdf, MCHBAR, base | 1, PCI_SIZE_32);
 185
 186        /*
 187         * The PCIEXBAR is assumed to live in the memory mapped IO space under
 188         * 4GiB
 189         */
 190        pci_x86_write_config(plat->bdf, PCIEXBAR + 4, 0, PCI_SIZE_32);
 191
 192        switch (plat->pciex_region_size >> 20) {
 193        default:
 194        case 256:
 195                region_size = PCIEXBAR_LENGTH_256MB;
 196                break;
 197        case 128:
 198                region_size = PCIEXBAR_LENGTH_128MB;
 199                break;
 200        case 64:
 201                region_size = PCIEXBAR_LENGTH_64MB;
 202                break;
 203        }
 204
 205        reg = CONFIG_MMCONF_BASE_ADDRESS | (region_size << 1)
 206                                | PCIEXBAR_PCIEXBAREN;
 207        pci_x86_write_config(plat->bdf, PCIEXBAR, reg, PCI_SIZE_32);
 208
 209        /*
 210         * TSEG defines the base of SMM range. BIOS determines the base
 211         * of TSEG memory which must be at or below Graphics base of GTT
 212         * Stolen memory, hence its better to clear TSEG register early
 213         * to avoid power on default non-zero value (if any).
 214         */
 215        pci_x86_write_config(plat->bdf, TSEG, 0, PCI_SIZE_32);
 216
 217        ret = apl_hostbridge_early_init_pinctrl(dev);
 218        if (ret)
 219                return log_msg_ret("pinctrl", ret);
 220
 221        return 0;
 222}
 223
 224static int apl_hostbridge_ofdata_to_platdata(struct udevice *dev)
 225{
 226        struct apl_hostbridge_platdata *plat = dev_get_platdata(dev);
 227        struct udevice *pinctrl;
 228        int ret;
 229
 230        /*
 231         * The host bridge holds the early pad data needed to get through TPL.
 232         * This is a small amount of data, enough to fit in TPL, so we keep it
 233         * separate from the full pad data, stored in the fsp-s subnode. That
 234         * subnode is not present in TPL, to save space.
 235         */
 236        ret = uclass_first_device_err(UCLASS_PINCTRL, &pinctrl);
 237        if (ret)
 238                return log_msg_ret("no hostbridge PINCTRL", ret);
 239#if !CONFIG_IS_ENABLED(OF_PLATDATA)
 240        int root;
 241
 242        /* Get length of PCI Express Region */
 243        plat->pciex_region_size = dev_read_u32_default(dev, "pciex-region-size",
 244                                                       256 << 20);
 245
 246        root = pci_get_devfn(dev);
 247        if (root < 0)
 248                return log_msg_ret("Cannot get host-bridge PCI address", root);
 249        plat->bdf = root;
 250
 251        ret = pinctrl_read_pads(pinctrl, dev_ofnode(dev), "early-pads",
 252                                &plat->early_pads, &plat->early_pads_count);
 253        if (ret)
 254                return log_msg_ret("early-pads", ret);
 255#else
 256        struct dtd_intel_apl_hostbridge *dtplat = &plat->dtplat;
 257        int size;
 258
 259        plat->pciex_region_size = dtplat->pciex_region_size;
 260        plat->bdf = pci_ofplat_get_devfn(dtplat->reg[0]);
 261
 262        /* Assume that if everything is 0, it is empty */
 263        plat->early_pads = dtplat->early_pads;
 264        size = ARRAY_SIZE(dtplat->early_pads);
 265        plat->early_pads_count = pinctrl_count_pads(pinctrl, plat->early_pads,
 266                                                    size);
 267
 268#endif
 269
 270        return 0;
 271}
 272
 273static int apl_hostbridge_probe(struct udevice *dev)
 274{
 275        if (spl_phase() == PHASE_TPL)
 276                return apl_hostbridge_early_init(dev);
 277
 278        return 0;
 279}
 280
 281static int apl_acpi_hb_get_name(const struct udevice *dev, char *out_name)
 282{
 283        return acpi_copy_name(out_name, "RHUB");
 284}
 285
 286#ifdef CONFIG_GENERATE_ACPI_TABLE
 287static int apl_acpi_hb_write_tables(const struct udevice *dev,
 288                                    struct acpi_ctx *ctx)
 289{
 290        struct acpi_table_header *header;
 291        struct acpi_dmar *dmar;
 292        u32 val;
 293
 294        /*
 295         * Create DMAR table only if virtualization is enabled. Due to some
 296         * constraints on Apollo Lake SoC (some stepping affected), VTD could
 297         * not be enabled together with IPU. Doing so will override and disable
 298         * VTD while leaving CAPID0_A still reporting that VTD is available.
 299         * As in this case FSP will lock VTD to disabled state, we need to make
 300         * sure that DMAR table generation only happens when at least DEFVTBAR
 301         * is enabled. Otherwise the DMAR header will be generated while the
 302         * content of the table will be missing.
 303         */
 304        dm_pci_read_config32(dev, CAPID0_A, &val);
 305        if ((val & VTD_DISABLE) ||
 306            !(readl(MCHBAR_REG(DEFVTBAR)) & VTBAR_ENABLED))
 307                return 0;
 308
 309        log_debug("ACPI:    * DMAR\n");
 310        dmar = (struct acpi_dmar *)ctx->current;
 311        header = &dmar->header;
 312        acpi_create_dmar(dmar, DMAR_INTR_REMAP);
 313        ctx->current += sizeof(struct acpi_dmar);
 314        apl_acpi_fill_dmar(ctx);
 315
 316        /* (Re)calculate length and checksum */
 317        header->length = ctx->current - (void *)dmar;
 318        header->checksum = table_compute_checksum((void *)dmar, header->length);
 319
 320        acpi_align(ctx);
 321        acpi_add_table(ctx, dmar);
 322
 323        return 0;
 324}
 325#endif
 326
 327static int apl_acpi_setup_nhlt(const struct udevice *dev, struct acpi_ctx *ctx)
 328{
 329        struct nhlt *nhlt = ctx->nhlt;
 330        u32 channels;
 331        ofnode node;
 332
 333        node = ofnode_find_subnode(dev_ofnode(dev), "nhlt");
 334        if (ofnode_read_u32(node, "intel,dmic-channels", &channels))
 335                return log_msg_ret("channels", -EINVAL);
 336        switch (channels) {
 337        case 1:
 338                return nhlt_add_endpoints(nhlt, dmic_1ch_descriptors,
 339                                          ARRAY_SIZE(dmic_1ch_descriptors));
 340        case 2:
 341                return nhlt_add_endpoints(nhlt, dmic_2ch_descriptors,
 342                                          ARRAY_SIZE(dmic_2ch_descriptors));
 343        case 4:
 344                return nhlt_add_endpoints(nhlt, dmic_4ch_descriptors,
 345                                          ARRAY_SIZE(dmic_4ch_descriptors));
 346        }
 347
 348        return log_msg_ret("channels", -EINVAL);
 349}
 350
 351static int apl_hostbridge_remove(struct udevice *dev)
 352{
 353        /*
 354         * TODO(sjg@chromium.org): Consider adding code from coreboot's
 355         * platform_fsp_notify_status()
 356         */
 357
 358        return 0;
 359}
 360
 361static ulong sa_read_reg(struct udevice *dev, int reg)
 362{
 363        u32 val;
 364
 365        /* All regions concerned for have 1 MiB alignment */
 366        dm_pci_read_config32(dev, BGSM, &val);
 367
 368        return ALIGN_DOWN(val, 1 << 20);
 369}
 370
 371ulong sa_get_tolud_base(struct udevice *dev)
 372{
 373        return sa_read_reg(dev, TOLUD);
 374}
 375
 376ulong sa_get_gsm_base(struct udevice *dev)
 377{
 378        return sa_read_reg(dev, BGSM);
 379}
 380
 381ulong sa_get_tseg_base(struct udevice *dev)
 382{
 383        return sa_read_reg(dev, TSEG);
 384}
 385
 386struct acpi_ops apl_hostbridge_acpi_ops = {
 387        .get_name       = apl_acpi_hb_get_name,
 388#ifdef CONFIG_GENERATE_ACPI_TABLE
 389        .write_tables   = apl_acpi_hb_write_tables,
 390#endif
 391        .setup_nhlt     = apl_acpi_setup_nhlt,
 392};
 393
 394static const struct udevice_id apl_hostbridge_ids[] = {
 395        { .compatible = "intel,apl-hostbridge" },
 396        { }
 397};
 398
 399U_BOOT_DRIVER(intel_apl_hostbridge) = {
 400        .name           = "intel_apl_hostbridge",
 401        .id             = UCLASS_NORTHBRIDGE,
 402        .of_match       = apl_hostbridge_ids,
 403        .ofdata_to_platdata = apl_hostbridge_ofdata_to_platdata,
 404        .probe          = apl_hostbridge_probe,
 405        .remove         = apl_hostbridge_remove,
 406        .platdata_auto_alloc_size = sizeof(struct apl_hostbridge_platdata),
 407        ACPI_OPS_PTR(&apl_hostbridge_acpi_ops)
 408        .flags          = DM_FLAG_OS_PREPARE,
 409};
 410