Merge patch series "Implement ACPI on aarch64"

Patrick Rudolph <patrick.rudolph@9elements.com> says:

Based on the existing work done by Simon Glass this series adds
support for booting aarch64 devices using ACPI only.
As first target QEMU SBSA support is added, which relies on ACPI
only to boot an OS. As secondary target the Raspberry Pi4 was used,
which is broadly available and allows easy testing of the proposed
solution.

The series is split into ACPI cleanups and code movements, adding
Arm specific ACPI tables and finally SoC and mainboard related
changes to boot a Linux on the QEMU SBSA and RPi4. Currently only the
mandatory ACPI tables are supported, allowing to boot into Linux
without errors.

The QEMU SBSA support is feature complete and provides the same
functionality as the EDK2 implementation.

The changes were tested on real hardware as well on QEMU v9.0:

qemu-system-aarch64 -machine sbsa-ref -nographic -cpu cortex-a57 \
                    -pflash secure-world.rom \
                    -pflash unsecure-world.rom

qemu-system-aarch64 -machine raspi4b -kernel u-boot.bin -cpu cortex-a72 \
-smp 4 -m 2G -drive file=raspbian.img,format=raw,index=0 \
-dtb bcm2711-rpi-4-b.dtb -nographic

Tested against FWTS V24.03.00.

Known issues:
- The QEMU rpi4 support is currently limited as it doesn't emulate PCI,
  USB or ethernet devices!
- The SMP bringup doesn't work on RPi4, but works in QEMU (Possibly
  cache related).
- PCI on RPI4 isn't working on real hardware since the pcie_brcmstb
  Linux kernel module doesn't support ACPI yet.

Link: https://lore.kernel.org/r/20241023132116.970117-1-patrick.rudolph@9elements.com
This commit is contained in:
Tom Rini
2024-10-27 17:14:22 -06:00
113 changed files with 6528 additions and 551 deletions

View File

@@ -78,14 +78,16 @@ config MTK_AHCI
Enable this driver to support Sata devices through
Mediatek AHCI controller (e.g. MT7622).
config AHCI_MVEBU
bool "Marvell EBU AHCI SATA support"
depends on ARCH_MVEBU || ARCH_OCTEON
config AHCI_GENERIC
bool "Generic AHCI SATA support"
depends on OF_CONTROL
select SCSI_AHCI
select SCSI
help
This option enables support for the Marvell EBU SoC's
onboard AHCI SATA.
This option enables support for generic onboard AHCI SATA controller
that do not need platform specific quirks, like emulated devices,
Marvell EBU SoC's onboard AHCI SATA controllers or Cavium's Octeon
7130 AHCI controllers.
If unsure, say N.

View File

@@ -14,6 +14,6 @@ obj-$(CONFIG_SATA) += sata.o sata_bootdev.o
obj-$(CONFIG_SATA_CEVA) += sata_ceva.o
obj-$(CONFIG_SATA_MV) += sata_mv.o
obj-$(CONFIG_SATA_SIL) += sata_sil.o
obj-$(CONFIG_AHCI_MVEBU) += ahci_mvebu.o
obj-$(CONFIG_AHCI_GENERIC) += ahci_generic.o
obj-$(CONFIG_SUNXI_AHCI) += ahci_sunxi.o
obj-$(CONFIG_MTK_AHCI) += mtk_ahci.o

View File

@@ -16,7 +16,7 @@ __weak int board_ahci_enable(void)
return 0;
}
static int mvebu_ahci_bind(struct udevice *dev)
static int generic_ahci_bind(struct udevice *dev)
{
struct udevice *scsi_dev;
int ret;
@@ -30,7 +30,7 @@ static int mvebu_ahci_bind(struct udevice *dev)
return 0;
}
static int mvebu_ahci_probe(struct udevice *dev)
static int generic_ahci_probe(struct udevice *dev)
{
/*
* Board specific SATA / AHCI enable code, e.g. enable the
@@ -43,18 +43,19 @@ static int mvebu_ahci_probe(struct udevice *dev)
return 0;
}
static const struct udevice_id mvebu_ahci_ids[] = {
static const struct udevice_id generic_ahci_ids[] = {
{ .compatible = "marvell,armada-380-ahci" },
{ .compatible = "marvell,armada-3700-ahci" },
{ .compatible = "marvell,armada-8k-ahci" },
{ .compatible = "cavium,octeon-7130-ahci" },
{ .compatible = "generic-ahci" },
{ }
};
U_BOOT_DRIVER(ahci_mvebu_drv) = {
.name = "ahci_mvebu",
U_BOOT_DRIVER(ahci_generic_drv) = {
.name = "ahci_generic",
.id = UCLASS_AHCI,
.of_match = mvebu_ahci_ids,
.bind = mvebu_ahci_bind,
.probe = mvebu_ahci_probe,
.of_match = generic_ahci_ids,
.bind = generic_ahci_bind,
.probe = generic_ahci_probe,
};

View File

@@ -48,6 +48,7 @@ enum method_t {
METHOD_FILL_SSDT,
METHOD_INJECT_DSDT,
METHOD_SETUP_NHLT,
METHOD_FILL_MADT,
};
/* Prototype for all methods */
@@ -282,6 +283,8 @@ 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_MADT:
return aops->fill_madt;
case METHOD_FILL_SSDT:
return aops->fill_ssdt;
case METHOD_INJECT_DSDT:
@@ -328,6 +331,19 @@ int acpi_recurse_method(struct acpi_ctx *ctx, struct udevice *parent,
return 0;
}
int acpi_fill_madt_subtbl(struct acpi_ctx *ctx)
{
int ret;
log_debug("Writing MADT table\n");
ret = acpi_recurse_method(ctx, dm_root(), METHOD_FILL_MADT, TYPE_NONE);
log_debug("Writing MADT finished, err=%d\n", ret);
if (ret)
return log_msg_ret("build", ret);
return ret;
}
int acpi_fill_ssdt(struct acpi_ctx *ctx)
{
void *start = ctx->current;

View File

@@ -26,6 +26,13 @@ config CPU_RISCV
help
Support CPU cores for RISC-V architecture.
config CPU_ARMV8
bool "Enable generic ARMv8 CPU driver"
depends on CPU && ARM64
select IRQ
help
Support CPU cores for armv8 architecture.
config CPU_MICROBLAZE
bool "Enable Microblaze CPU driver"
depends on CPU && MICROBLAZE

View File

@@ -6,10 +6,12 @@
obj-$(CONFIG_CPU) += cpu-uclass.o
obj-$(CONFIG_ARCH_BCM283X) += bcm283x_cpu.o
obj-$(CONFIG_ARCH_BMIPS) += bmips_cpu.o
obj-$(CONFIG_ARCH_IMX8) += imx8_cpu.o
obj-$(CONFIG_ARCH_AT91) += at91_cpu.o
obj-$(CONFIG_ARCH_MEDIATEK) += mtk_cpu.o
obj-$(CONFIG_CPU_ARMV8) += armv8_cpu.o
obj-$(CONFIG_CPU_IMX) += imx8_cpu.o
obj-$(CONFIG_CPU_MPC83XX) += mpc83xx_cpu.o
obj-$(CONFIG_CPU_RISCV) += riscv_cpu.o

151
drivers/cpu/armv8_cpu.c Normal file
View File

@@ -0,0 +1,151 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright 2024 9elements GmbH
*/
#include <cpu.h>
#include <dm.h>
#include <irq.h>
#include <acpi/acpigen.h>
#include <asm/armv8/cpu.h>
#include <asm/io.h>
#include <dm/acpi.h>
#include <linux/bitops.h>
#include <linux/printk.h>
#include <linux/sizes.h>
static int armv8_cpu_get_desc(const struct udevice *dev, char *buf, int size)
{
int cpuid;
cpuid = (read_midr() & MIDR_PARTNUM_MASK) >> MIDR_PARTNUM_SHIFT;
snprintf(buf, size, "CPU MIDR %04x", cpuid);
return 0;
}
static int armv8_cpu_get_info(const struct udevice *dev,
struct cpu_info *info)
{
info->cpu_freq = 0;
info->features = BIT(CPU_FEAT_L1_CACHE) | BIT(CPU_FEAT_MMU);
return 0;
}
static int armv8_cpu_get_count(const struct udevice *dev)
{
return uclass_id_count(UCLASS_CPU);
}
#ifdef CONFIG_ACPIGEN
int armv8_cpu_fill_ssdt(const struct udevice *dev, struct acpi_ctx *ctx)
{
uint core_id = dev_seq(dev);
acpigen_write_processor_device(ctx, core_id);
return 0;
}
int armv8_cpu_fill_madt(const struct udevice *dev, struct acpi_ctx *ctx)
{
struct acpi_madt_gicc *gicc;
struct cpu_plat *cpu_plat;
struct udevice *gic;
u64 gicc_gicv = 0;
u64 gicc_gich = 0;
u64 gicc_gicr_base = 0;
u64 gicc_phys_base = 0;
u32 gicc_perf_gsiv = 0;
u64 gicc_mpidr;
u32 gicc_vgic_maint_irq = 0;
int addr_index;
fdt_addr_t addr;
int ret;
struct irq req_irq;
cpu_plat = dev_get_parent_plat(dev);
if (!cpu_plat)
return 0;
ret = irq_get_interrupt_parent(dev, &gic);
if (ret) {
log_err("%s: Failed to find interrupt parent for %s\n",
__func__, dev->name);
return -ENODEV;
}
addr_index = 1;
if (device_is_compatible(gic, "arm,gic-v3")) {
addr = dev_read_addr_index(gic, addr_index++);
if (addr != FDT_ADDR_T_NONE)
gicc_gicr_base = addr;
}
addr = dev_read_addr_index(gic, addr_index++);
if (addr != FDT_ADDR_T_NONE)
gicc_phys_base = addr;
addr = dev_read_addr_index(gic, addr_index++);
if (addr != FDT_ADDR_T_NONE)
gicc_gich = addr;
addr = dev_read_addr_index(gic, addr_index++);
if (addr != FDT_ADDR_T_NONE)
gicc_gicv = addr;
ret = irq_get_by_index(gic, 0, &req_irq);
if (!ret)
gicc_vgic_maint_irq = req_irq.id;
gicc_mpidr = dev_read_u64_default(dev, "reg", 0);
if (!gicc_mpidr)
gicc_mpidr = dev_read_u32_default(dev, "reg", 0);
/*
* gicc_vgic_maint_irq and gicc_gicv are the same for every CPU
*/
gicc = ctx->current;
acpi_write_madt_gicc(gicc,
dev_seq(dev),
gicc_perf_gsiv, /* FIXME: needs a PMU driver */
gicc_phys_base,
gicc_gicv,
gicc_gich,
gicc_vgic_maint_irq,
gicc_gicr_base,
gicc_mpidr,
0); /* FIXME: Not defined in DT */
acpi_inc(ctx, gicc->length);
return 0;
}
struct acpi_ops armv8_cpu_acpi_ops = {
.fill_ssdt = armv8_cpu_fill_ssdt,
.fill_madt = armv8_cpu_fill_madt,
};
#endif
static const struct cpu_ops cpu_ops = {
.get_count = armv8_cpu_get_count,
.get_desc = armv8_cpu_get_desc,
.get_info = armv8_cpu_get_info,
};
static const struct udevice_id cpu_ids[] = {
{ .compatible = "arm,armv8" },
{}
};
U_BOOT_DRIVER(arm_cpu) = {
.name = "arm-cpu",
.id = UCLASS_CPU,
.of_match = cpu_ids,
.ops = &cpu_ops,
.flags = DM_FLAG_PRE_RELOC,
ACPI_OPS_PTR(&armv8_cpu_acpi_ops)
};

31
drivers/cpu/armv8_cpu.h Normal file
View File

@@ -0,0 +1,31 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright 2024 9elements GmbH
*/
#include <dm/acpi.h>
#include <dm/device.h>
#ifndef _ARMV8_CPU_H_
#define _ARMV8_CPU_H_
/**
* armv8_cpu_fill_ssdt() - Fill the SSDT
* Parses the FDT and writes the SSDT nodes.
*
* @dev: cpu device to generate ACPI tables for
* @ctx: ACPI context pointer
* @return: 0 if OK, or a negative error code.
*/
int armv8_cpu_fill_ssdt(const struct udevice *dev, struct acpi_ctx *ctx);
/**
* armv8_cpu_fill_madt() - Fill the MADT
* Parses the FDT and writes the MADT subtables.
*
* @dev: cpu device to generate ACPI tables for
* @ctx: ACPI context pointer
* @return: 0 if OK, or a negative error code.
*/
int armv8_cpu_fill_madt(const struct udevice *dev, struct acpi_ctx *ctx);
#endif

214
drivers/cpu/bcm283x_cpu.c Normal file
View File

@@ -0,0 +1,214 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright 2024 9elements GmbH
*/
#include <cpu.h>
#include <cpu_func.h>
#include <dm.h>
#include <fdt_support.h>
#include <acpi/acpigen.h>
#include <asm/armv8/cpu.h>
#include <asm/cache.h>
#include <asm/io.h>
#include <asm/global_data.h>
#include <asm/system.h>
#include <asm-generic/sections.h>
#include <linux/bitops.h>
#include <linux/clk-provider.h>
#include <linux/delay.h>
#include "armv8_cpu.h"
DECLARE_GLOBAL_DATA_PTR;
struct bcm_plat {
u64 release_addr;
};
static int cpu_bcm_get_desc(const struct udevice *dev, char *buf, int size)
{
struct cpu_plat *plat = dev_get_parent_plat(dev);
const char *name;
if (size < 32)
return -ENOSPC;
if (device_is_compatible(dev, "arm,cortex-a53"))
name = "A53";
else if (device_is_compatible(dev, "arm,cortex-a72"))
name = "A72";
else
name = "?";
snprintf(buf, size, "Broadcom Cortex-%s at %u MHz\n",
name, plat->timebase_freq);
return 0;
}
static int cpu_bcm_get_info(const struct udevice *dev, struct cpu_info *info)
{
struct cpu_plat *plat = dev_get_parent_plat(dev);
info->cpu_freq = plat->timebase_freq * 1000;
info->features = BIT(CPU_FEAT_L1_CACHE) | BIT(CPU_FEAT_MMU);
return 0;
}
static int cpu_bcm_get_count(const struct udevice *dev)
{
return uclass_id_count(UCLASS_CPU);
}
static int cpu_bcm_get_vendor(const struct udevice *dev, char *buf, int size)
{
snprintf(buf, size, "Broadcom");
return 0;
}
static int cpu_bcm_is_current(struct udevice *dev)
{
struct cpu_plat *plat = dev_get_parent_plat(dev);
if (plat->cpu_id == (read_mpidr() & 0xffff))
return 1;
return 0;
}
/**
* bcm_cpu_on - Releases the secondary CPU from it's spintable
*
* Write the CPU's spintable mailbox and let the CPU enter U-Boot.
*
* @dev: Device to start
* @return: zero on success or error code on failure.
*/
static int bcm_cpu_on(struct udevice *dev)
{
struct bcm_plat *plat = dev_get_plat(dev);
ulong *start_address;
if (plat->release_addr == ~0ULL)
return -ENODATA;
start_address = map_physmem(plat->release_addr, sizeof(uintptr_t), MAP_NOCACHE);
/* Point secondary CPU to U-Boot entry */
*start_address = (uintptr_t)_start;
/* Make sure the other CPUs see the written start address */
if (!CONFIG_IS_ENABLED(SYS_DCACHE_OFF))
flush_dcache_all();
/* Send an event to wake up the secondary CPU. */
asm("dsb ishst\n"
"sev");
unmap_physmem(start_address, MAP_NOCACHE);
return 0;
}
static const struct cpu_ops cpu_bcm_ops = {
.get_desc = cpu_bcm_get_desc,
.get_info = cpu_bcm_get_info,
.get_count = cpu_bcm_get_count,
.get_vendor = cpu_bcm_get_vendor,
.is_current = cpu_bcm_is_current,
};
static const struct udevice_id cpu_bcm_ids[] = {
{ .compatible = "arm,cortex-a53" }, /* RPi 3 */
{ .compatible = "arm,cortex-a72" }, /* RPi 4 */
{ }
};
static int bcm_cpu_bind(struct udevice *dev)
{
struct cpu_plat *plat = dev_get_parent_plat(dev);
plat->cpu_id = dev_read_addr(dev);
return 0;
}
/**
* bcm_cpu_of_to_plat - Gather spin-table release address
*
* Read the spin-table release address to allow all seconary CPUs to enter
* U-Boot when necessary.
*
* @dev: Device to start
*/
static int bcm_cpu_of_to_plat(struct udevice *dev)
{
struct bcm_plat *plat = dev_get_plat(dev);
const char *prop;
if (CONFIG_IS_ENABLED(ARMV8_MULTIENTRY)) {
plat->release_addr = ~0ULL;
prop = dev_read_string(dev, "enable-method");
if (!prop || strcmp(prop, "spin-table"))
return -ENODEV;
plat->release_addr = dev_read_u64_default(dev, "cpu-release-addr", ~0ULL);
if (plat->release_addr == ~0ULL)
return -ENODEV;
}
return 0;
}
static int bcm_cpu_probe(struct udevice *dev)
{
struct cpu_plat *plat = dev_get_parent_plat(dev);
struct clk clk;
int ret;
/* Get a clock if it exists */
ret = clk_get_by_index(dev, 0, &clk);
if (!ret) {
ret = clk_enable(&clk);
if (ret && (ret != -ENOSYS || ret != -EOPNOTSUPP))
return ret;
ret = clk_get_rate(&clk);
if (IS_ERR_VALUE(ret))
return ret;
plat->timebase_freq = ret;
}
/*
* The armstub holds the secondary CPUs in a spinloop. When
* ARMV8_MULTIENTRY is enabled release the secondary CPUs and
* let them enter U-Boot as well.
*/
if (CONFIG_IS_ENABLED(ARMV8_MULTIENTRY)) {
ret = bcm_cpu_on(dev);
if (ret)
return ret;
}
return ret;
}
struct acpi_ops bcm283x_cpu_acpi_ops = {
.fill_ssdt = armv8_cpu_fill_ssdt,
.fill_madt = armv8_cpu_fill_madt,
};
U_BOOT_DRIVER(cpu_bcm_drv) = {
.name = "bcm283x_cpu",
.id = UCLASS_CPU,
.of_match = cpu_bcm_ids,
.ops = &cpu_bcm_ops,
.probe = bcm_cpu_probe,
.bind = bcm_cpu_bind,
.of_to_plat = bcm_cpu_of_to_plat,
.plat_auto = sizeof(struct bcm_plat),
ACPI_OPS_PTR(&bcm283x_cpu_acpi_ops)
};

View File

@@ -62,6 +62,40 @@ int irq_read_and_clear(struct irq *irq)
return ops->read_and_clear(irq);
}
int irq_get_interrupt_parent(const struct udevice *dev,
struct udevice **interrupt_parent)
{
struct ofnode_phandle_args phandle_args;
struct udevice *irq = NULL;
ofnode node;
int ret;
if (!dev || !interrupt_parent)
return -EINVAL;
*interrupt_parent = NULL;
node = dev_ofnode(dev);
if (!ofnode_valid(node))
return -EINVAL;
while (ofnode_valid(node)) {
ret = ofnode_parse_phandle_with_args(node, "interrupt-parent",
NULL, 0, 0, &phandle_args);
if (!ret && !device_get_global_by_ofnode(phandle_args.node, &irq))
break;
node = ofnode_get_parent(node);
}
if (!irq) {
log_err("Cannot find an interrupt parent for device %s\n", dev->name);
return -ENODEV;
}
*interrupt_parent = irq;
return 0;
}
#if CONFIG_IS_ENABLED(OF_PLATDATA)
int irq_get_by_phandle(struct udevice *dev, const struct phandle_2_arg *cells,
struct irq *irq)
@@ -142,10 +176,40 @@ err:
int irq_get_by_index(struct udevice *dev, int index, struct irq *irq)
{
struct ofnode_phandle_args args;
int ret;
struct udevice *interrupt_parent;
int ret, size, i;
const __be32 *list;
u32 count;
ret = dev_read_phandle_with_args(dev, "interrupts-extended",
"#interrupt-cells", 0, index, &args);
if (ret) {
list = dev_read_prop(dev, "interrupts", &size);
if (!list)
return -ENOENT;
ret = irq_get_interrupt_parent(dev, &interrupt_parent);
if (ret)
return -ENODEV;
args.node = dev_ofnode(interrupt_parent);
if (dev_read_u32(interrupt_parent, "#interrupt-cells", &count)) {
log_err("%s: could not get #interrupt-cells for %s\n",
__func__, dev->name);
return -ENOENT;
}
if (index * count >= size / sizeof(*list))
return -ENOENT;
if (count > OF_MAX_PHANDLE_ARGS)
count = OF_MAX_PHANDLE_ARGS;
args.args_count = count;
for (i = 0; i < count; i++)
args.args[i] = be32_to_cpup(&list[index * count + i]);
return irq_get_by_index_tail(ret, dev_ofnode(dev), &args,
"interrupts", index, irq);
}
return irq_get_by_index_tail(ret, dev_ofnode(dev), &args,
"interrupts-extended", index > 0, irq);

View File

@@ -12,6 +12,7 @@
* Copyright (C) 2020 Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
*/
#include <asm/arch/acpi/bcm2711.h>
#include <errno.h>
#include <dm.h>
#include <dm/ofnode.h>
@@ -21,88 +22,6 @@
#include <linux/log2.h>
#include <linux/iopoll.h>
/* Offset of the mandatory PCIe capability config registers */
#define BRCM_PCIE_CAP_REGS 0x00ac
/* The PCIe controller register offsets */
#define PCIE_RC_CFG_VENDOR_SPECIFIC_REG1 0x0188
#define VENDOR_SPECIFIC_REG1_ENDIAN_MODE_BAR2_MASK 0xc
#define VENDOR_SPECIFIC_REG1_LITTLE_ENDIAN 0x0
#define PCIE_RC_CFG_PRIV1_ID_VAL3 0x043c
#define CFG_PRIV1_ID_VAL3_CLASS_CODE_MASK 0xffffff
#define PCIE_RC_CFG_PRIV1_LINK_CAPABILITY 0x04dc
#define PCIE_RC_CFG_PRIV1_LINK_CAPABILITY_ASPM_SUPPORT_MASK 0xc00
#define PCIE_RC_DL_MDIO_ADDR 0x1100
#define PCIE_RC_DL_MDIO_WR_DATA 0x1104
#define PCIE_RC_DL_MDIO_RD_DATA 0x1108
#define PCIE_MISC_MISC_CTRL 0x4008
#define MISC_CTRL_SCB_ACCESS_EN_MASK 0x1000
#define MISC_CTRL_CFG_READ_UR_MODE_MASK 0x2000
#define MISC_CTRL_MAX_BURST_SIZE_MASK 0x300000
#define MISC_CTRL_MAX_BURST_SIZE_128 0x0
#define MISC_CTRL_SCB0_SIZE_MASK 0xf8000000
#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_LO 0x400c
#define PCIE_MEM_WIN0_LO(win) \
PCIE_MISC_CPU_2_PCIE_MEM_WIN0_LO + ((win) * 4)
#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_HI 0x4010
#define PCIE_MEM_WIN0_HI(win) \
PCIE_MISC_CPU_2_PCIE_MEM_WIN0_HI + ((win) * 4)
#define PCIE_MISC_RC_BAR1_CONFIG_LO 0x402c
#define RC_BAR1_CONFIG_LO_SIZE_MASK 0x1f
#define PCIE_MISC_RC_BAR2_CONFIG_LO 0x4034
#define RC_BAR2_CONFIG_LO_SIZE_MASK 0x1f
#define PCIE_MISC_RC_BAR2_CONFIG_HI 0x4038
#define PCIE_MISC_RC_BAR3_CONFIG_LO 0x403c
#define RC_BAR3_CONFIG_LO_SIZE_MASK 0x1f
#define PCIE_MISC_PCIE_STATUS 0x4068
#define STATUS_PCIE_PORT_MASK 0x80
#define STATUS_PCIE_PORT_SHIFT 7
#define STATUS_PCIE_DL_ACTIVE_MASK 0x20
#define STATUS_PCIE_DL_ACTIVE_SHIFT 5
#define STATUS_PCIE_PHYLINKUP_MASK 0x10
#define STATUS_PCIE_PHYLINKUP_SHIFT 4
#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT 0x4070
#define MEM_WIN0_BASE_LIMIT_LIMIT_MASK 0xfff00000
#define MEM_WIN0_BASE_LIMIT_BASE_MASK 0xfff0
#define MEM_WIN0_BASE_LIMIT_BASE_HI_SHIFT 12
#define PCIE_MEM_WIN0_BASE_LIMIT(win) \
PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT + ((win) * 4)
#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_HI 0x4080
#define MEM_WIN0_BASE_HI_BASE_MASK 0xff
#define PCIE_MEM_WIN0_BASE_HI(win) \
PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_HI + ((win) * 8)
#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_LIMIT_HI 0x4084
#define PCIE_MEM_WIN0_LIMIT_HI_LIMIT_MASK 0xff
#define PCIE_MEM_WIN0_LIMIT_HI(win) \
PCIE_MISC_CPU_2_PCIE_MEM_WIN0_LIMIT_HI + ((win) * 8)
#define PCIE_MISC_HARD_PCIE_HARD_DEBUG 0x4204
#define PCIE_HARD_DEBUG_SERDES_IDDQ_MASK 0x08000000
#define PCIE_MSI_INTR2_CLR 0x4508
#define PCIE_MSI_INTR2_MASK_SET 0x4510
#define PCIE_EXT_CFG_DATA 0x8000
#define PCIE_EXT_CFG_INDEX 0x9000
#define PCIE_RGR1_SW_INIT_1 0x9210
#define RGR1_SW_INIT_1_PERST_MASK 0x1
#define RGR1_SW_INIT_1_INIT_MASK 0x2
/* PCIe parameters */
#define BRCM_NUM_PCIE_OUT_WINS 4
@@ -447,7 +366,7 @@ static int brcm_pcie_probe(struct udevice *dev)
* This will need to be changed when support for other SoCs is added.
*/
setbits_le32(base + PCIE_RGR1_SW_INIT_1,
RGR1_SW_INIT_1_INIT_MASK | RGR1_SW_INIT_1_PERST_MASK);
PCIE_RGR1_SW_INIT_1_INIT_MASK | PCIE_RGR1_SW_INIT_1_PERST_MASK);
/*
* The delay is a safety precaution to preclude the reset signal
* from looking like a glitch.
@@ -455,7 +374,7 @@ static int brcm_pcie_probe(struct udevice *dev)
udelay(100);
/* Take the bridge out of reset */
clrbits_le32(base + PCIE_RGR1_SW_INIT_1, RGR1_SW_INIT_1_INIT_MASK);
clrbits_le32(base + PCIE_RGR1_SW_INIT_1, PCIE_RGR1_SW_INIT_1_INIT_MASK);
clrbits_le32(base + PCIE_MISC_HARD_PCIE_HARD_DEBUG,
PCIE_HARD_DEBUG_SERDES_IDDQ_MASK);
@@ -508,7 +427,7 @@ static int brcm_pcie_probe(struct udevice *dev)
/* Unassert the fundamental reset */
clrbits_le32(pcie->base + PCIE_RGR1_SW_INIT_1,
RGR1_SW_INIT_1_PERST_MASK);
PCIE_RGR1_SW_INIT_1_PERST_MASK);
/*
* Wait for 100ms after PERST# deassertion; see PCIe CEM specification
@@ -552,7 +471,7 @@ static int brcm_pcie_probe(struct udevice *dev)
* a PCIe-PCIe bridge (the default setting is to be EP mode).
*/
clrsetbits_le32(base + PCIE_RC_CFG_PRIV1_ID_VAL3,
CFG_PRIV1_ID_VAL3_CLASS_CODE_MASK, 0x060400);
PCIE_RC_CFG_PRIV1_ID_VAL3_CLASS_CODE_MASK, 0x060400);
if (pcie->ssc) {
ret = brcm_pcie_set_ssc(pcie->base);
@@ -570,8 +489,8 @@ static int brcm_pcie_probe(struct udevice *dev)
nlw, ssc_good ? "(SSC)" : "(!SSC)");
/* PCIe->SCB endian mode for BAR */
clrsetbits_le32(base + PCIE_RC_CFG_VENDOR_SPECIFIC_REG1,
VENDOR_SPECIFIC_REG1_ENDIAN_MODE_BAR2_MASK,
clrsetbits_le32(base + PCIE_RC_CFG_VENDOR_VENDOR_SPECIFIC_REG1,
PCIE_RC_CFG_VENDOR_VENDOR_SPECIFIC_REG1_ENDIAN_MODE_BAR2_MASK,
VENDOR_SPECIFIC_REG1_LITTLE_ENDIAN);
/*
@@ -584,7 +503,7 @@ static int brcm_pcie_probe(struct udevice *dev)
* let's instead just unadvertise ASPM support.
*/
clrbits_le32(base + PCIE_RC_CFG_PRIV1_LINK_CAPABILITY,
PCIE_RC_CFG_PRIV1_LINK_CAPABILITY_ASPM_SUPPORT_MASK);
LINK_CAPABILITY_ASPM_SUPPORT_MASK);
return 0;
}
@@ -595,14 +514,14 @@ static int brcm_pcie_remove(struct udevice *dev)
void __iomem *base = pcie->base;
/* Assert fundamental reset */
setbits_le32(base + PCIE_RGR1_SW_INIT_1, RGR1_SW_INIT_1_PERST_MASK);
setbits_le32(base + PCIE_RGR1_SW_INIT_1, PCIE_RGR1_SW_INIT_1_PERST_MASK);
/* Turn off SerDes */
setbits_le32(base + PCIE_MISC_HARD_PCIE_HARD_DEBUG,
PCIE_HARD_DEBUG_SERDES_IDDQ_MASK);
/* Shutdown bridge */
setbits_le32(base + PCIE_RGR1_SW_INIT_1, RGR1_SW_INIT_1_INIT_MASK);
setbits_le32(base + PCIE_RGR1_SW_INIT_1, PCIE_RGR1_SW_INIT_1_INIT_MASK);
return 0;
}

View File

@@ -19,6 +19,7 @@
#include <watchdog.h>
#include <asm/io.h>
#include <serial.h>
#include <spl.h>
#include <dm/device_compat.h>
#include <dm/platform_data/serial_pl01x.h>
#include <linux/compiler.h>
@@ -272,6 +273,28 @@ __weak struct serial_device *default_serial_console(void)
return &pl01x_serial_drv;
}
#else
static int pl01x_serial_getinfo(struct udevice *dev,
struct serial_device_info *info)
{
struct pl01x_serial_plat *plat = dev_get_plat(dev);
/* save code size */
if (!not_xpl())
return -ENOSYS;
info->type = SERIAL_CHIP_PL01X;
info->addr_space = SERIAL_ADDRESS_SPACE_MEMORY;
info->addr = plat->base;
info->size = 0x1000;
info->reg_width = 4;
info->reg_shift = 2;
info->reg_offset = 0;
info->clock = plat->clock;
return 0;
}
int pl01x_serial_setbrg(struct udevice *dev, int baudrate)
{
struct pl01x_serial_plat *plat = dev_get_plat(dev);
@@ -341,6 +364,7 @@ static const struct dm_serial_ops pl01x_serial_ops = {
.pending = pl01x_serial_pending,
.getc = pl01x_serial_getc,
.setbrg = pl01x_serial_setbrg,
.getinfo = pl01x_serial_getinfo,
};
#if CONFIG_IS_ENABLED(OF_REAL)

View File

@@ -68,6 +68,14 @@ config USB_XHCI_MVEBU
SoCs, which includes Armada8K, Armada3700 and other Armada
family SoCs.
config USB_XHCI_GENERIC
bool "Generic SoC USB 3.0 support"
depends on OF_CONTROL
default n
help
Choose this option to add support for USB 3.0 driver for SoCs
that do not need platform specific code, like on emulated targets.
config USB_XHCI_OCTEON
bool "Support for Marvell Octeon family on-chip xHCI USB controller"
depends on ARCH_OCTEON

View File

@@ -50,6 +50,7 @@ obj-$(CONFIG_USB_XHCI_EXYNOS) += xhci-exynos5.o
obj-$(CONFIG_USB_XHCI_FSL) += xhci-fsl.o
obj-$(CONFIG_USB_XHCI_MTK) += xhci-mtk.o
obj-$(CONFIG_USB_XHCI_MVEBU) += xhci-mvebu.o
obj-$(CONFIG_USB_XHCI_GENERIC) += xhci-generic.o
obj-$(CONFIG_USB_XHCI_OMAP) += xhci-omap.o
obj-$(CONFIG_USB_XHCI_PCI) += xhci-pci.o
obj-$(CONFIG_USB_XHCI_RCAR) += xhci-rcar.o

View File

@@ -0,0 +1,75 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2024 9elements GmbH
*
* GENERIC USB HOST xHCI Controller
*/
#include <dm.h>
#include <fdtdec.h>
#include <log.h>
#include <usb.h>
#include <asm/io.h>
#include <dm/device_compat.h>
#include <usb/xhci.h>
struct generic_xhci_plat {
fdt_addr_t hcd_base;
};
/**
* Contains pointers to register base addresses
* for the usb controller.
*/
struct generic_xhci {
struct xhci_ctrl ctrl; /* Needs to come first in this struct! */
struct usb_plat usb_plat;
struct xhci_hccr *hcd;
};
static int xhci_usb_probe(struct udevice *dev)
{
struct generic_xhci_plat *plat = dev_get_plat(dev);
struct generic_xhci *ctx = dev_get_priv(dev);
struct xhci_hcor *hcor;
int len;
ctx->hcd = (struct xhci_hccr *)phys_to_virt(plat->hcd_base);
len = HC_LENGTH(xhci_readl(&ctx->hcd->cr_capbase));
hcor = (struct xhci_hcor *)((uintptr_t)ctx->hcd + len);
return xhci_register(dev, ctx->hcd, hcor);
}
static int xhci_usb_of_to_plat(struct udevice *dev)
{
struct generic_xhci_plat *plat = dev_get_plat(dev);
/*
* Get the base address for XHCI controller from the device node
*/
plat->hcd_base = dev_read_addr(dev);
if (plat->hcd_base == FDT_ADDR_T_NONE) {
dev_dbg(dev, "Can't get the XHCI register base address\n");
return -ENXIO;
}
return 0;
}
static const struct udevice_id xhci_usb_ids[] = {
{ .compatible = "generic-xhci" },
{ }
};
U_BOOT_DRIVER(usb_xhci) = {
.name = "xhci_generic",
.id = UCLASS_USB,
.of_match = xhci_usb_ids,
.of_to_plat = xhci_usb_of_to_plat,
.probe = xhci_usb_probe,
.remove = xhci_deregister,
.ops = &xhci_usb_ops,
.plat_auto = sizeof(struct generic_xhci_plat),
.priv_auto = sizeof(struct generic_xhci),
.flags = DM_FLAG_ALLOC_PRIV_DMA,
};