From 98663e5cbcd96ec4fdd6b88e93a031322d508900 Mon Sep 17 00:00:00 2001 From: Dj <43033058+DjDeveloperr@users.noreply.github.com> Date: Mon, 26 Oct 2026 02:34:16 -0400 Subject: [PATCH] PCI: apple: Add T6050 APIF hosts and native AIC MSI Add preinitialized T6050 APIF PCIe hosts with AIC MSI, requester-to-stream validation, bounded reset sequencing, and ownership-aware teardown. Adapt IOMMU map lookup to the current device-tree API. Source snapshot: t6050-gpu ee68abb105200e99636a5edfc647a5cfc1d89954 Integration base: 9cbdd0734d9e186c5f5f5949a21b909d10c2571b Signed-off-by: Dj <43033058+DjDeveloperr@users.noreply.github.com> --- .../bindings/pci/apple,apif-pcie.yaml | 119 +++++ drivers/pci/controller/Kconfig | 11 + drivers/pci/controller/Makefile | 1 + drivers/pci/controller/apple-apif/Makefile | 2 + drivers/pci/controller/apple-apif/host.c | 220 ++++++++++ drivers/pci/controller/apple-apif/t6050.c | 415 ++++++++++++++++++ drivers/pci/controller/apple-apif/t6050.h | 10 + .../apple-apif/t6050_pcie_disable.c | 102 +++++ .../apple-apif/t6050_pcie_disable.h | 9 + .../controller/apple-apif/t6050_pcie_gpio.c | 32 ++ .../controller/apple-apif/t6050_pcie_gpio.h | 12 + .../controller/apple-apif/t6050_pcie_msi.c | 28 ++ .../controller/apple-apif/t6050_pcie_port.c | 255 +++++++++++ .../apple-apif/t6050_pcie_sequence.h | 39 ++ drivers/pci/probe.c | 7 + include/linux/pci.h | 1 + tools/testing/selftests/aurora-apif/pcie.py | 7 + 17 files changed, 1270 insertions(+) create mode 100644 Documentation/devicetree/bindings/pci/apple,apif-pcie.yaml create mode 100644 drivers/pci/controller/apple-apif/Makefile create mode 100644 drivers/pci/controller/apple-apif/host.c create mode 100644 drivers/pci/controller/apple-apif/t6050.c create mode 100644 drivers/pci/controller/apple-apif/t6050.h create mode 100644 drivers/pci/controller/apple-apif/t6050_pcie_disable.c create mode 100644 drivers/pci/controller/apple-apif/t6050_pcie_disable.h create mode 100644 drivers/pci/controller/apple-apif/t6050_pcie_gpio.c create mode 100644 drivers/pci/controller/apple-apif/t6050_pcie_gpio.h create mode 100644 drivers/pci/controller/apple-apif/t6050_pcie_msi.c create mode 100644 drivers/pci/controller/apple-apif/t6050_pcie_port.c create mode 100644 drivers/pci/controller/apple-apif/t6050_pcie_sequence.h create mode 100755 tools/testing/selftests/aurora-apif/pcie.py diff --git a/Documentation/devicetree/bindings/pci/apple,apif-pcie.yaml b/Documentation/devicetree/bindings/pci/apple,apif-pcie.yaml new file mode 100644 index 0000000000000..7a14a96f6236f --- /dev/null +++ b/Documentation/devicetree/bindings/pci/apple,apif-pcie.yaml @@ -0,0 +1,119 @@ +# SPDX-License-Identifier: GPL-2.0-only +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pci/apple,apif-pcie.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Preinitialized Apple PCIe host in an Aura guest +maintainers: + - Dj + +description: | + The boot producer configures the controller, PHY, link, apertures and + RID-to-SID routing before guest entry. It disables endpoint bus mastering + and MSI before transferring ownership. Linux uses ECAM and allocates MSI + from the native AIC range; it does not run raw cold-init register sequences. + +allOf: + - $ref: /schemas/pci/pci-host-bridge.yaml# + - if: + properties: + compatible: + contains: + const: apple,t6050-pcie-apif + then: + required: + - apple,rid-to-sid + - max-link-speed + - num-lanes + properties: + reg: + minItems: 5 + reg-names: + minItems: 5 + else: + properties: + reg: + maxItems: 1 + reg-names: + maxItems: 1 + apple,rid-to-sid: false + +properties: + compatible: + oneOf: + - const: apple,apif-pcie + - items: + - const: apple,t6050-pcie-apif + - const: apple,apif-pcie + reg: + minItems: 1 + items: + - description: ECAM configuration window + - description: T6050 port-0 control registers, at least 32 KiB + - description: T6050 port-0 PHY registers, at least 16 KiB + - description: Active-low PERST pad register, at least four bytes + - description: T6050 interrupt-to-AXI registers, at least 16 KiB + reg-names: + minItems: 1 + items: + - const: ecam + - const: port + - const: phy + - const: perst + - const: intr2axi + apple,preinitialized: + type: boolean + description: Controller is initialized and endpoint DMA/MSI is stopped + apple,msi-address: + $ref: /schemas/types.yaml#/definitions/uint64 + description: Native MSI doorbell; 4 KiB aligned for the T6050 layout + msi-ranges: + maxItems: 1 + description: One four-cell native AIC edge interrupt range and vector count + apple,rid-to-sid: + $ref: /schemas/types.yaml#/definitions/uint32-matrix + minItems: 1 + maxItems: 15 + items: + items: + - description: Hardware routing slot + minimum: 1 + maximum: 15 + - description: Requester ID + maximum: 65535 + - description: Firmware-assigned DART stream ID + maximum: 255 + description: Restore existing routing after the optional preboot port cycle + iommu-map: true + iommu-map-mask: true + max-link-speed: + const: 3 + num-lanes: + const: 1 + dma-coherent: true + +patternProperties: + '^apple,(apcie-config|pcie-rc|pcie-rc-gen[345]-shadow)-tunables$': + $ref: /schemas/types.yaml#/definitions/uint32-matrix + maxItems: 16 + items: + items: + - description: Register offset + - const: 4 + - description: Clear mask + - description: Set mask + description: Boot-producer register tuples for the matched layout + +required: + - compatible + - reg + - reg-names + - ranges + - bus-range + - apple,preinitialized + - apple,msi-address + - msi-ranges + - iommu-map + +unevaluatedProperties: false diff --git a/drivers/pci/controller/Kconfig b/drivers/pci/controller/Kconfig index b758e0682ffd8..ae25823e8917f 100644 --- a/drivers/pci/controller/Kconfig +++ b/drivers/pci/controller/Kconfig @@ -417,3 +417,14 @@ source "drivers/pci/controller/dwc/Kconfig" source "drivers/pci/controller/mobiveil/Kconfig" source "drivers/pci/controller/plda/Kconfig" endmenu + +config PCIE_APPLE_APIF + tristate "Preinitialized Apple PCIe host for Aura guests" + depends on ARM64 && OF && PCI_MSI + depends on APPLE_DART_APIF + select PCI_HOST_COMMON + help + Admit an ECAM host initialized by the boot producer. Allocate MSI + vectors from the native AIC range described in its guest device tree. + A matching T6050 layout additionally supports a controlled preboot + port transition; other layouts never enter that sequence. diff --git a/drivers/pci/controller/Makefile b/drivers/pci/controller/Makefile index 3bd9cdb5b1197..5c993ba81b6ff 100644 --- a/drivers/pci/controller/Makefile +++ b/drivers/pci/controller/Makefile @@ -68,3 +68,4 @@ endif endif obj-$(CONFIG_PCIE_APPLE_PIODMA_DIAG) += pcie-apple-piodma-diag.o +obj-$(CONFIG_PCIE_APPLE_APIF) += apple-apif/ diff --git a/drivers/pci/controller/apple-apif/Makefile b/drivers/pci/controller/apple-apif/Makefile new file mode 100644 index 0000000000000..0f043a0791e4b --- /dev/null +++ b/drivers/pci/controller/apple-apif/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_PCIE_APPLE_APIF) += pcie-apple-apif.o +pcie-apple-apif-y := host.o t6050.o t6050_pcie_port.o t6050_pcie_disable.o t6050_pcie_gpio.o t6050_pcie_msi.o diff --git a/drivers/pci/controller/apple-apif/host.c b/drivers/pci/controller/apple-apif/host.c new file mode 100644 index 0000000000000..db61d07fe9cba --- /dev/null +++ b/drivers/pci/controller/apple-apif/host.c @@ -0,0 +1,220 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Preinitialized PCI host with native AIC MSI. + * Hierarchical MSI allocation follows pcie-apple.c: + * Copyright (C) 2021 Alyssa Rosenzweig + * Copyright (C) 2021 Google LLC + * Copyright (C) 2021 Corellium LLC + * Copyright (C) 2021 Mark Kettenis + * Original authors: Alyssa Rosenzweig + * Marc Zyngier + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "t6050.h" +#include +#include "../pci-host-common.h" + +struct apif_pcie { + struct t6050_pcie *t6050; + struct mutex lock; + unsigned long *bitmap; + u32 nvecs; + u64 doorbell; + struct irq_fwspec fwspec; + struct irq_domain *msi_domain; +}; + +static void apif_msi_compose(struct irq_data *data, struct msi_msg *msg) +{ + struct apif_pcie *pcie = irq_data_get_irq_chip_data(data); + + msg->address_hi = upper_32_bits(pcie->doorbell); + msg->address_lo = lower_32_bits(pcie->doorbell); + msg->data = data->hwirq; +} + +static struct irq_chip apif_msi_chip = { + .name = "apif-PCI-MSI", + .irq_mask = irq_chip_mask_parent, + .irq_unmask = irq_chip_unmask_parent, + .irq_eoi = irq_chip_eoi_parent, + .irq_set_affinity = irq_chip_set_affinity_parent, + .irq_set_type = irq_chip_set_type_parent, + .irq_compose_msi_msg = apif_msi_compose, +}; + +static int apif_msi_alloc(struct irq_domain *domain, unsigned int virq, + unsigned int nr_irqs, void *args) +{ + struct apif_pcie *pcie = domain->host_data; + struct irq_fwspec fwspec = pcie->fwspec; + int hwirq, ret; + + mutex_lock(&pcie->lock); + hwirq = bitmap_find_free_region(pcie->bitmap, pcie->nvecs, + order_base_2(nr_irqs)); + mutex_unlock(&pcie->lock); + if (hwirq < 0) + return -ENOSPC; + + fwspec.param[fwspec.param_count - 2] += hwirq; + ret = irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, &fwspec); + if (ret) { + mutex_lock(&pcie->lock); + bitmap_release_region(pcie->bitmap, hwirq, + order_base_2(nr_irqs)); + mutex_unlock(&pcie->lock); + return ret; + } + for (unsigned int i = 0; i < nr_irqs; i++) + irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i, + &apif_msi_chip, pcie); + return 0; +} + +static void apif_msi_free(struct irq_domain *domain, unsigned int virq, + unsigned int nr_irqs) +{ + struct irq_data *data = irq_domain_get_irq_data(domain, virq); + struct apif_pcie *pcie = domain->host_data; + + irq_domain_free_irqs_parent(domain, virq, nr_irqs); + mutex_lock(&pcie->lock); + bitmap_release_region(pcie->bitmap, data->hwirq, order_base_2(nr_irqs)); + mutex_unlock(&pcie->lock); +} + +static const struct irq_domain_ops apif_msi_domain_ops = { + .alloc = apif_msi_alloc, + .free = apif_msi_free, +}; + +static const struct msi_parent_ops apif_msi_parent_ops = { + .supported_flags = MSI_GENERIC_FLAGS_MASK | MSI_FLAG_PCI_MSIX | + MSI_FLAG_MULTI_PCI_MSI, + .required_flags = MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | + MSI_FLAG_PCI_MSI_MASK_PARENT, + .chip_flags = MSI_CHIP_FLAG_SET_EOI, + .bus_select_token = DOMAIN_BUS_PCI_MSI, + .init_dev_msi_info = msi_lib_init_dev_msi_info, +}; + +static void apif_msi_cleanup(void *data) +{ + struct apif_pcie *pcie = data; + + irq_domain_remove(pcie->msi_domain); +} + +static int apif_pcie_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct pci_host_bridge *bridge; + struct apif_pcie *pcie; + struct of_phandle_args args; + struct irq_domain_info info = {}; + int ret; + + if (!of_property_read_bool(dev->of_node, "apple,preinitialized")) + return dev_err_probe(dev, -EINVAL, + "missing stopped boot handoff\n"); + bridge = devm_pci_alloc_host_bridge(dev, sizeof(*pcie)); + if (!bridge) + return -ENOMEM; + pcie = pci_host_bridge_priv(bridge); + mutex_init(&pcie->lock); + + ret = of_property_read_u64(dev->of_node, "apple,msi-address", + &pcie->doorbell); + if (ret || (pcie->doorbell & 0xf)) + return dev_err_probe(dev, -EINVAL, + "missing/unaligned MSI doorbell\n"); + ret = of_parse_phandle_with_args(dev->of_node, "msi-ranges", + "#interrupt-cells", 0, &args); + if (ret) + return dev_err_probe(dev, ret, "missing MSI IRQ range\n"); + if (of_property_count_u32_elems(dev->of_node, "msi-ranges") != 6) { + of_node_put(args.np); + return -EINVAL; + } + + if (args.args_count != 4 || args.args[0] != 0 || + args.args[3] != IRQ_TYPE_EDGE_RISING) { + of_node_put(args.np); + return dev_err_probe(dev, -EINVAL, + "MSI range must use AIC edge IRQs\n"); + } + ret = of_property_read_u32_index(dev->of_node, "msi-ranges", 5, + &pcie->nvecs); + if (ret || !is_power_of_2(pcie->nvecs) || pcie->nvecs > 256 || + args.args[2] > U32_MAX - pcie->nvecs) { + of_node_put(args.np); + return dev_err_probe(dev, -EINVAL, + "invalid MSI vector count\n"); + } + of_phandle_args_to_fwspec(args.np, args.args, args.args_count, + &pcie->fwspec); + of_node_put(args.np); + info.parent = irq_find_matching_fwspec(&pcie->fwspec, DOMAIN_BUS_WIRED); + if (!info.parent) + return -EPROBE_DEFER; + pcie->bitmap = devm_bitmap_zalloc(dev, pcie->nvecs, GFP_KERNEL); + if (!pcie->bitmap) + return -ENOMEM; + info.fwnode = dev_fwnode(dev); + info.ops = &apif_msi_domain_ops; + info.size = pcie->nvecs; + info.host_data = pcie; + pcie->msi_domain = + msi_create_parent_irq_domain(&info, &apif_msi_parent_ops); + if (!pcie->msi_domain) + return -ENOMEM; + ret = devm_add_action_or_reset(dev, apif_msi_cleanup, pcie); + if (ret) + return ret; + pcie->t6050 = t6050_pcie_init(pdev, pcie->doorbell, pcie->nvecs); + if (IS_ERR(pcie->t6050)) + return PTR_ERR(pcie->t6050); + return pci_host_common_init(pdev, bridge, &pci_generic_ecam_ops); +} + +int apple_apif_pcie_cycle(struct pci_dev *pdev) +{ + struct pci_host_bridge *bridge = pci_find_host_bridge(pdev->bus); + struct apif_pcie *pcie; + + if (!bridge || !bridge->dev.parent || + !of_device_is_compatible(bridge->dev.parent->of_node, + "apple,apif-pcie")) + return -ENODEV; + pcie = pci_host_bridge_priv(bridge); + if (!pcie->t6050) + return -EOPNOTSUPP; + return t6050_pcie_cycle(pcie->t6050, pdev); +} +EXPORT_SYMBOL_GPL(apple_apif_pcie_cycle); + +static const struct of_device_id apif_pcie_match[] = { + { .compatible = "apple,apif-pcie" }, + {}, +}; +MODULE_DEVICE_TABLE(of, apif_pcie_match); + +static struct platform_driver apif_pcie_driver = { + .probe = apif_pcie_probe, + .remove = pci_host_common_remove, + .driver = { + .name = "pcie-apple-apif", + .of_match_table = apif_pcie_match, + }, +}; +module_platform_driver(apif_pcie_driver); +MODULE_DESCRIPTION("apif preinitialized Apple PCIe host with native AIC MSI"); +MODULE_LICENSE("GPL"); diff --git a/drivers/pci/controller/apple-apif/t6050.c b/drivers/pci/controller/apple-apif/t6050.c new file mode 100644 index 0000000000000..fd84731266523 --- /dev/null +++ b/drivers/pci/controller/apple-apif/t6050.c @@ -0,0 +1,415 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include +#include +#include +#include +#include +#include "t6050.h" +#include "t6050_pcie_sequence.h" +#include "t6050_pcie_disable.h" +#include "t6050_pcie_gpio.h" + +struct t6050_pcie { + struct device *dev; + struct mutex lock; + void __iomem *port, *phy, *gpio, *intr2axi; + struct pci_dev *bridge, *endpoint; + struct t6050_pcie_io io; + struct t6050_gpio_pad_io pad; + u64 doorbell; + unsigned int nvecs; + u32 routes[3 * 64]; + unsigned int nroutes; +}; + +static void __iomem *t6050_reg(struct t6050_pcie *pcie, unsigned int reg, + unsigned int off) +{ + if (off & 3) + return NULL; + switch (reg) { + case T6050_PCIE_PORT0: + return off < 0x8000 ? pcie->port + off : NULL; + case T6050_PCIE_PORT0_PHY: + return off < 0x4000 ? pcie->phy + off : NULL; + case T6050_PCIE_PORT0_INTR2AXI: + return off < 0x4000 ? pcie->intr2axi + off : NULL; + default: + return NULL; + } +} + +static int t6050_read(void *ctx, unsigned int reg, unsigned int off, u32 *value) +{ + struct t6050_pcie *pcie = ctx; + void __iomem *addr; + if (reg == T6050_PCIE_ECAM) + return pcie->bridge ? + pcibios_err_to_errno(pci_read_config_dword( + pcie->bridge, off, value)) : + -ENODEV; + addr = t6050_reg(pcie, reg, off); + if (!addr) + return -EINVAL; + *value = readl(addr); + return 0; +} + +static int t6050_write(void *ctx, unsigned int reg, unsigned int off, u32 value) +{ + struct t6050_pcie *pcie = ctx; + void __iomem *addr; + if (reg == T6050_PCIE_ECAM) + return pcie->bridge ? + pcibios_err_to_errno(pci_write_config_dword( + pcie->bridge, off, value)) : + -ENODEV; + addr = t6050_reg(pcie, reg, off); + if (!addr) + return -EINVAL; + writel(value, addr); + return 0; +} + +static int t6050_read16(void *ctx, unsigned int reg, unsigned int off, + u16 *value) +{ + struct t6050_pcie *pcie = ctx; + if (reg != T6050_PCIE_ECAM || !pcie->bridge) + return -EINVAL; + return pcibios_err_to_errno( + pci_read_config_word(pcie->bridge, off, value)); +} + +static int t6050_write16(void *ctx, unsigned int reg, unsigned int off, + u16 value) +{ + struct t6050_pcie *pcie = ctx; + if (reg != T6050_PCIE_ECAM || !pcie->bridge) + return -EINVAL; + return pcibios_err_to_errno( + pci_write_config_word(pcie->bridge, off, value)); +} + +static int t6050_poll(void *ctx, unsigned int reg, unsigned int off, u32 mask, + u32 expected, unsigned int timeout) +{ + struct t6050_pcie *pcie = ctx; + u32 value = 0; + int ret, err; + ret = read_poll_timeout(t6050_read, err, + err || (value & mask) == expected, 1, timeout, + false, ctx, reg, off, &value); + if (ret || err) + dev_err(pcie->dev, + "port wait reg%u+%#x value=%#x mask=%#x expected=%#x\n", + reg, off, value, mask, expected); + return err ?: ret; +} + +static void t6050_delay(void *ctx, unsigned int us) +{ + if (us <= 10) + udelay(us); + else + usleep_range(us, us + max(1U, us / 10)); +} + +static int t6050_tune(void *ctx, const char *name, unsigned int reg) +{ + struct t6050_pcie *pcie = ctx; + char property[64]; + u32 cells[64], old; + int count, ret; + snprintf(property, sizeof(property), "apple,%s", name); + if (!of_find_property(pcie->dev->of_node, property, NULL)) + return 0; + count = of_property_count_u32_elems(pcie->dev->of_node, property); + if (count <= 0 || count > ARRAY_SIZE(cells) || count % 4) + return -EINVAL; + ret = of_property_read_u32_array(pcie->dev->of_node, property, cells, + count); + if (ret) + return ret; + /* Validate every entry before the first mutation. */ + for (int i = 0; i < count; i += 4) + if (cells[i + 1] != 4 || (cells[i] & 3) || + (reg == T6050_PCIE_ECAM ? + cells[i] >= PCI_CFG_SPACE_EXP_SIZE : + !t6050_reg(pcie, reg, cells[i]))) + return -EINVAL; + for (int i = 0; i < count; i += 4) { + ret = t6050_read(ctx, reg, cells[i], &old); + if (ret) + return ret; + ret = t6050_write(ctx, reg, cells[i], + (old & ~cells[i + 2]) | cells[i + 3]); + if (ret) + return ret; + } + return 0; +} + +static int t6050_gpio_read(void *ctx, u32 *value) +{ + *value = readl(((struct t6050_pcie *)ctx)->gpio); + return 0; +} +static int t6050_gpio_write(void *ctx, u32 value) +{ + writel(value, ((struct t6050_pcie *)ctx)->gpio); + return 0; +} +static int t6050_reset(void *ctx, bool asserted) +{ + return t6050_pcie_gpio_perst(&((struct t6050_pcie *)ctx)->pad, + asserted); +} +static int t6050_force_available(void *ctx, bool on) +{ + return apple_dart_apif_set_power( + &((struct t6050_pcie *)ctx)->endpoint->dev, on); +} + +struct t6050_pcie *t6050_pcie_init(struct platform_device *pdev, u64 doorbell, + unsigned int nvecs) +{ + struct t6050_pcie *pcie; + struct resource *r; + u32 speed, lanes; + int ret; + + if (!of_device_is_compatible(pdev->dev.of_node, + "apple,t6050-pcie-apif")) + return NULL; + if (of_property_read_u32(pdev->dev.of_node, "max-link-speed", &speed) || + of_property_read_u32(pdev->dev.of_node, "num-lanes", &lanes) || + speed != 3 || lanes != 1) + return ERR_PTR(-EINVAL); + pcie = devm_kzalloc(&pdev->dev, sizeof(*pcie), GFP_KERNEL); + if (!pcie) + return ERR_PTR(-ENOMEM); + pcie->dev = &pdev->dev; + pcie->doorbell = doorbell; + pcie->nvecs = nvecs; + ret = of_property_count_u32_elems(pdev->dev.of_node, + "apple,rid-to-sid"); + if (ret <= 0 || ret > ARRAY_SIZE(pcie->routes) || ret % 3) + return ERR_PTR(-EINVAL); + pcie->nroutes = ret / 3; + ret = of_property_read_u32_array(pdev->dev.of_node, "apple,rid-to-sid", + pcie->routes, ret); + if (ret) + return ERR_PTR(ret); + for (unsigned int i = 0; i < pcie->nroutes; i++) { + u32 *r = &pcie->routes[3 * i]; + if (!r[0] || r[0] >= 16 || r[1] > 0xffff || r[0] != r[2]) + return ERR_PTR(-EINVAL); + struct device_node *target; + struct of_phandle_args iommu; + u32 sid; + + ret = of_map_iommu_id(pdev->dev.of_node, r[1], &iommu); + target = ret ? NULL : iommu.np; + sid = ret || !iommu.args_count ? U32_MAX : iommu.args[0]; + if (ret || !target || + !of_device_is_compatible(target, "apple,dart-apif") || + sid != r[2]) { + of_node_put(target); + return ERR_PTR(-EINVAL); + } + of_node_put(target); + for (unsigned int j = 0; j < i; j++) + if (r[0] == pcie->routes[3 * j] || + r[1] == pcie->routes[3 * j + 1]) + return ERR_PTR(-EINVAL); + } + + mutex_init(&pcie->lock); + pcie->port = devm_platform_ioremap_resource_byname(pdev, "port"); + pcie->phy = devm_platform_ioremap_resource_byname(pdev, "phy"); + pcie->gpio = devm_platform_ioremap_resource_byname(pdev, "perst"); + pcie->intr2axi = + devm_platform_ioremap_resource_byname(pdev, "intr2axi"); + if (IS_ERR(pcie->port)) + return ERR_CAST(pcie->port); + if (IS_ERR(pcie->phy)) + return ERR_CAST(pcie->phy); + if (IS_ERR(pcie->gpio)) + return ERR_CAST(pcie->gpio); + if (IS_ERR(pcie->intr2axi)) + return ERR_CAST(pcie->intr2axi); + r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "port"); + if (resource_size(r) < 0x8000) + return ERR_PTR(-EINVAL); + for (unsigned int i = 0; i < 3; i++) { + static const char *const names[] = { "phy", "perst", + "intr2axi" }; + r = platform_get_resource_byname(pdev, IORESOURCE_MEM, + names[i]); + if (!r || resource_size(r) < (i == 1 ? 4 : 0x4000)) + return ERR_PTR(-EINVAL); + } + pcie->pad = (struct t6050_gpio_pad_io){ .context = pcie, + .read = t6050_gpio_read, + .write = t6050_gpio_write }; + pcie->io = (struct t6050_pcie_io){ .context = pcie, + .read = t6050_read, + .write = t6050_write, + .poll = t6050_poll, + .apply_tunables = t6050_tune, + .apply_port_tunables = t6050_tune, + .read16 = t6050_read16, + .write16 = t6050_write16, + .delay = t6050_delay, + .endpoint_reset = t6050_reset }; + + ret = t6050_pcie_configure_msi_port0(&pcie->io, pcie->doorbell, + pcie->nvecs); + return ret ? ERR_PTR(ret) : pcie; +} + +int t6050_pcie_cycle(struct t6050_pcie *pcie, struct pci_dev *endpoint) +{ + struct pci_dev *bridge = endpoint->bus->self, *sibling; + u32 value, identity; + u16 command, pmctrl; + int ret, pm; + + if (!bridge || endpoint->vendor != PCI_VENDOR_ID_APPLE || + (endpoint->device != 0x1900 && endpoint->device != 0x1901)) + return -ENODEV; + + if (!pci_trylock_rescan_remove()) + return -EAGAIN; + mutex_lock(&pcie->lock); + list_for_each_entry(sibling, &endpoint->bus->devices, bus_list) { + if (sibling != endpoint) { + ret = -EBUSY; + goto out; + } + } + pcie->bridge = bridge; + pcie->endpoint = endpoint; + ret = pci_save_state(bridge); + if (ret) + goto out; + ret = pci_save_state(endpoint); + if (ret) + goto out; + ret = pcibios_err_to_errno( + pci_read_config_word(endpoint, PCI_COMMAND, &command)); + if (ret) + goto out; + pm = pci_find_capability(endpoint, PCI_CAP_ID_PM); + if (!pm) { + ret = -ENODEV; + goto out; + } + /* This API is only for the isolated ROM/preboot function. */ + pci_clear_master(endpoint); + ret = pcibios_err_to_errno( + pci_read_config_word(endpoint, PCI_COMMAND, &pmctrl)); + if (ret || (pmctrl & PCI_COMMAND_MASTER)) { + ret = ret ?: -EIO; + goto stopped; + } + if (!pci_wait_for_pending_transaction(endpoint)) { + ret = -EBUSY; + goto stopped; + } + ret = pcibios_err_to_errno( + pci_read_config_word(endpoint, pm + PCI_PM_CTRL, &pmctrl)); + if (ret) + goto stopped; + ret = pcibios_err_to_errno(pci_write_config_word( + endpoint, pm + PCI_PM_CTRL, + (pmctrl & ~PCI_PM_CTRL_STATE_MASK) | PCI_D3hot | + PCI_PM_CTRL_PME_ENABLE)); + if (ret) + goto stopped; + usleep_range(10000, 11000); + + ret = pcibios_err_to_errno( + pci_write_config_word(bridge, PCI_COMMAND, 0)); + if (ret) + goto stopped; + ret = pcibios_err_to_errno( + pci_read_config_dword(bridge, PCI_PRIMARY_BUS, &value)); + if (ret) + goto stopped; + ret = pcibios_err_to_errno(pci_write_config_dword( + bridge, PCI_PRIMARY_BUS, value & 0xff0000ff)); + if (ret) + goto stopped; + value = readl(pcie->port + 0x800); + writel(value | 0x100, pcie->port + 0x800); + writel(0x11, pcie->port + 0x88); + + ret = t6050_poll(pcie, T6050_PCIE_PORT0, 0x88, 1, 0, 500000); + if (ret) + goto stopped; + ret = t6050_poll(pcie, T6050_PCIE_PORT0, 0x208, BIT(6), BIT(6), 500000); + if (ret) + goto stopped; + ret = t6050_reset(pcie, true); + if (ret) + goto stopped; + writel(readl(pcie->phy) & ~BIT(31), pcie->phy); + ret = t6050_pcie_disable_port0(&pcie->io, t6050_force_available, + 250000); + if (ret) + goto stopped; + ret = t6050_pcie_enable_port0(&pcie->io, 250000); + if (ret) + goto stopped; + ret = t6050_pcie_configure_port0(&pcie->io, 3); + if (ret) + goto stopped; + + for (unsigned int i = 0; i < pcie->nroutes; i++) { + u32 *r = &pcie->routes[3 * i]; + writel(BIT(31) | (r[2] << 16) | r[1], + pcie->port + 0x3000 + 4 * r[0]); + } + ret = t6050_pcie_configure_msi_port0(&pcie->io, pcie->doorbell, + pcie->nvecs); + if (ret) + goto stopped; + ret = t6050_force_available(pcie, true); + if (ret) + goto stopped; + ret = t6050_pcie_train_port0(&pcie->io, 500000); + if (ret) + goto stopped; + pci_restore_state(bridge); + ret = pcibios_err_to_errno( + pci_read_config_dword(endpoint, PCI_VENDOR_ID, &identity)); + if (ret) + goto stopped; + if (identity != 0x1900106b && identity != 0x1901106b) { + ret = -ENODEV; + goto stopped; + } + pci_restore_state(endpoint); + ret = pcibios_err_to_errno(pci_write_config_word( + endpoint, pm + PCI_PM_CTRL, pmctrl & ~PCI_PM_CTRL_STATE_MASK)); + if (ret) + goto stopped; + usleep_range(10000, 11000); + ret = pcibios_err_to_errno( + pci_write_config_word(endpoint, PCI_COMMAND, command)); + if (ret) + goto stopped; + goto out; +stopped: + /* A partial transition never restores endpoint DMA or bridge decoding. */ + pci_clear_master(endpoint); + pci_write_config_word(bridge, PCI_COMMAND, 0); + dev_err(&endpoint->dev, "T6050 port cycle stopped: %d\n", ret); +out: + pcie->endpoint = pcie->bridge = NULL; + mutex_unlock(&pcie->lock); + pci_unlock_rescan_remove(); + return ret; +} diff --git a/drivers/pci/controller/apple-apif/t6050.h b/drivers/pci/controller/apple-apif/t6050.h new file mode 100644 index 0000000000000..a45249c24a9b4 --- /dev/null +++ b/drivers/pci/controller/apple-apif/t6050.h @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only +#ifndef _APIF_T6050_H +#define _APIF_T6050_H +#include +#include +struct t6050_pcie; +struct t6050_pcie *t6050_pcie_init(struct platform_device *pdev, u64 doorbell, + unsigned int nvecs); +int t6050_pcie_cycle(struct t6050_pcie *pcie, struct pci_dev *endpoint); +#endif diff --git a/drivers/pci/controller/apple-apif/t6050_pcie_disable.c b/drivers/pci/controller/apple-apif/t6050_pcie_disable.c new file mode 100644 index 0000000000000..a8989d7f7805f --- /dev/null +++ b/drivers/pci/controller/apple-apif/t6050_pcie_disable.c @@ -0,0 +1,102 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include +#include "t6050_pcie_disable.h" + +static int update(const struct t6050_pcie_io *io, unsigned int reg, + unsigned int off, u32 clear, u32 set) +{ + u32 v; + int ret = io->read(io->context, reg, off, &v); + return ret ? ret : io->write(io->context, reg, off, (v & ~clear) | set); +} + +int t6050_pcie_disable_port0(const struct t6050_pcie_io *io, + int (*force_available)(void *, bool), + unsigned int timeout_us) +{ + u32 v; + unsigned int elapsed; + int ret; + if (!io || !io->read || !io->write || !io->poll || !io->delay || + !force_available || !timeout_us) + return -EINVAL; + ret = force_available(io->context, false); + if (ret) + return ret; + + ret = io->read(io->context, T6050_PCIE_PORT0, 0x804, &v); + if (ret) + return ret; + if (!(v & 4)) { + ret = io->poll(io->context, T6050_PCIE_PORT0, 0x820, 4, 4, + timeout_us < 10000 ? timeout_us : 10000); + if (ret) + return ret; + } + + for (elapsed = 0;; elapsed++) { + ret = io->read(io->context, T6050_PCIE_PORT0, 0x820, &v); + if (ret) + return ret; + if (!(v & 0x1ff) || !(v & 0xff0000)) + break; + if (elapsed >= timeout_us || elapsed >= 10000) + return -ETIMEDOUT; + io->delay(io->context, 1); + } + ret = update(io, T6050_PCIE_PORT0, 0x13c, 0, 0x100); + if (ret) + return ret; + + for (elapsed = 0;; elapsed += 5) { + ret = io->read(io->context, T6050_PCIE_ECAM, 0x728, &v); + if (ret) + return ret; + if ((v & 0x3f) != 0x14) + break; + if (elapsed >= timeout_us || elapsed >= 5000) + return -ETIMEDOUT; + io->delay(io->context, 5); + } + + ret = io->poll(io->context, T6050_PCIE_PORT0, 0xa8, 1, 1, + timeout_us < 10000 ? timeout_us : 10000); + if (ret) + return ret; + ret = update(io, T6050_PCIE_PORT0, 0x82c, 0, 1u << 24); + if (ret) + return ret; + ret = io->poll(io->context, T6050_PCIE_PORT0, 0xa8, 1u << 16, 1u << 16, + timeout_us < 2000 ? timeout_us : 2000); + if (ret) + return ret; + ret = update(io, T6050_PCIE_PORT0, 0x82c, 0, 1u << 16); + if (ret) + return ret; + io->delay(io->context, 1); + ret = update(io, T6050_PCIE_PORT0, 0x82c, 1, 0); + if (ret) + return ret; + io->delay(io->context, 1); + ret = update(io, T6050_PCIE_PORT0, 0x82c, 1u << 24, 0); + if (ret) + return ret; + ret = update(io, T6050_PCIE_PORT0, 0x13c, 0x100, 0); + if (ret) + return ret; + static const u32 clear[] = { 1u << 31, 1u << 10, 1u << 9, 0, 1, 2 }; + for (unsigned int i = 0; i < sizeof(clear) / sizeof(clear[0]); i++) { + ret = update(io, T6050_PCIE_PORT0_PHY, 0, clear[i], + i == 3 ? 0x10 : 0); + if (ret) + return ret; + } + ret = io->write(io->context, T6050_PCIE_PORT0, 0x100, (~0U)); + if (ret) + return ret; + ret = update(io, T6050_PCIE_PORT0, 0x800, 1, 0); + if (ret) + return ret; + + return io->poll(io->context, T6050_PCIE_PORT0, 0x804, 1, 0, timeout_us); +} diff --git a/drivers/pci/controller/apple-apif/t6050_pcie_disable.h b/drivers/pci/controller/apple-apif/t6050_pcie_disable.h new file mode 100644 index 0000000000000..c42f777204efb --- /dev/null +++ b/drivers/pci/controller/apple-apif/t6050_pcie_disable.h @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-only +#ifndef T6050_PCIE_DISABLE_H +#define T6050_PCIE_DISABLE_H +#include "t6050_pcie_sequence.h" + +int t6050_pcie_disable_port0(const struct t6050_pcie_io *io, + int (*force_available)(void *, bool), + unsigned int timeout_us); +#endif diff --git a/drivers/pci/controller/apple-apif/t6050_pcie_gpio.c b/drivers/pci/controller/apple-apif/t6050_pcie_gpio.c new file mode 100644 index 0000000000000..8828aa54dda8c --- /dev/null +++ b/drivers/pci/controller/apple-apif/t6050_pcie_gpio.c @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include +#include "t6050_pcie_gpio.h" + +int t6050_pcie_gpio_perst(const struct t6050_gpio_pad_io *io, bool asserted) +{ + u32 old, desired, first, verify; + int ret; + if (!io || !io->read || !io->write) + return -EINVAL; + ret = io->read(io->context, &old); + if (ret) + return ret; + + if (old & (1u << 21)) + return -EIO; + desired = (old & ~0x27fu) | 0x202u | !asserted; + first = (desired & ~0x200u) | (old & 0x200u); + ret = io->write(io->context, first); + if (ret) + return ret; + if (!(old & 0x200u)) { + ret = io->write(io->context, desired); + if (ret) + return ret; + } + ret = io->read(io->context, &verify); + if (ret) + return ret; + + return ((verify ^ desired) & 0x27fu) ? -EIO : 0; +} diff --git a/drivers/pci/controller/apple-apif/t6050_pcie_gpio.h b/drivers/pci/controller/apple-apif/t6050_pcie_gpio.h new file mode 100644 index 0000000000000..d55d0b3301b1a --- /dev/null +++ b/drivers/pci/controller/apple-apif/t6050_pcie_gpio.h @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: GPL-2.0-only +#ifndef T6050_PCIE_GPIO_H +#define T6050_PCIE_GPIO_H +#include + +struct t6050_gpio_pad_io { + void *context; + int (*read)(void *, u32 *); + int (*write)(void *, u32); +}; +int t6050_pcie_gpio_perst(const struct t6050_gpio_pad_io *, bool asserted); +#endif diff --git a/drivers/pci/controller/apple-apif/t6050_pcie_msi.c b/drivers/pci/controller/apple-apif/t6050_pcie_msi.c new file mode 100644 index 0000000000000..0d7e46878e240 --- /dev/null +++ b/drivers/pci/controller/apple-apif/t6050_pcie_msi.c @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include +#include "t6050_pcie_sequence.h" + +int t6050_pcie_configure_msi_port0(const struct t6050_pcie_io *io, u64 doorbell, + unsigned int count) +{ + int ret; + if (!io || !io->write || (doorbell & 0xfff) || !count || count > 256) + return -EINVAL; + ret = io->write(io->context, T6050_PCIE_PORT0, 0x124, 1); + if (ret) + return ret; + ret = io->write(io->context, T6050_PCIE_PORT0, 0x16c, (u32)doorbell); + if (ret) + return ret; + ret = io->write(io->context, T6050_PCIE_PORT0, 0x170, + (u32)(doorbell >> 32)); + if (ret) + return ret; + for (unsigned int i = 0; i < count; i++) { + ret = io->write(io->context, T6050_PCIE_PORT0, 0x3800 + 4 * i, + 0x80000000 | i); + if (ret) + return ret; + } + return 0; +} diff --git a/drivers/pci/controller/apple-apif/t6050_pcie_port.c b/drivers/pci/controller/apple-apif/t6050_pcie_port.c new file mode 100644 index 0000000000000..3095179a95eeb --- /dev/null +++ b/drivers/pci/controller/apple-apif/t6050_pcie_port.c @@ -0,0 +1,255 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include +#include "t6050_pcie_sequence.h" + +static int rmw(const struct t6050_pcie_io *io, unsigned int reg, + unsigned int off, u32 clear, u32 set) +{ + u32 v; + int ret = io->read(io->context, reg, off, &v); + return ret ? ret : io->write(io->context, reg, off, (v & ~clear) | set); +} + +int t6050_pcie_reset_port0(const struct t6050_pcie_io *io) +{ + static const struct { + unsigned int off; + u32 value; + } first[] = { + {0x88,0x110}, {0x100,0xffffffff}, {0x148,0xffffffff}, {0x210,0xffffffff}, + {0x80,0}, {0x84,0}, {0x104,0xfffffff0}, {0x124,0x100}, {0x16c,0}, + {0x13c,0}, {0x800,0x100100}, {0x808,0x1001ff}, {0x82c,0x10000}, + }, last[] = { + {0x130,0x03020000}, {0x140,0x10}, {0x144,0x253770}, + {0x21c,0}, {0x834,0}, {0x83c,0}, + }; + int ret; + if (!io || !io->write) + return -EINVAL; + + for (unsigned int i = 0; i < sizeof(first) / sizeof(first[0]); i++) { + ret = io->write(io->context, T6050_PCIE_PORT0, first[i].off, + first[i].value); + if (ret) + return ret; + } + for (unsigned int i = 0; i < 64; i++) { + ret = io->write(io->context, T6050_PCIE_PORT0, 0x3000 + 4 * i, + 0); + if (ret) + return ret; + } + for (unsigned int i = 0; i < 256; i++) { + ret = io->write(io->context, T6050_PCIE_PORT0, 0x3800 + 4 * i, + 0); + if (ret) + return ret; + } + for (unsigned int i = 0; i < sizeof(last) / sizeof(last[0]); i++) { + ret = io->write(io->context, T6050_PCIE_PORT0, last[i].off, + last[i].value); + if (ret) + return ret; + } + return 0; +} + +int t6050_pcie_enable_port0(const struct t6050_pcie_io *io, + unsigned int timeout_us) +{ + u32 v; + unsigned int waited = 0; + int ret; + if (!io || !io->read || !io->write || !io->poll || !io->delay || + !io->apply_port_tunables || !timeout_us) + return -EINVAL; + ret = t6050_pcie_reset_port0(io); + if (ret) + return ret; + + ret = io->read(io->context, T6050_PCIE_PORT0, 0x82c, &v); + if (ret) + return ret; + if (!(v & (1u << 16))) + return -EIO; + ret = io->apply_port_tunables(io->context, "apcie-config-tunables", + T6050_PCIE_PORT0); + if (ret) + return ret; + ret = rmw(io, T6050_PCIE_PORT0, 0x800, 0, 1); + if (ret) + return ret; + + for (;;) { + ret = io->read(io->context, T6050_PCIE_PORT0, 0xa8, &v); + if (ret) + return ret; + if (!(v & 1) || !(v & (1u << 16))) + break; + if (waited++ >= timeout_us || waited > 10000) + return -ETIMEDOUT; + io->delay(io->context, 1); + } + ret = rmw(io, T6050_PCIE_PORT0, 0x82c, 0, 1); + if (ret) + return ret; + ret = io->poll(io->context, T6050_PCIE_PORT0, 0xa8, 1, 1, + timeout_us < 20000 ? timeout_us : 20000); + if (ret) + return ret; + ret = rmw(io, T6050_PCIE_PORT0, 0x82c, 1u << 16, 0); + if (ret) + return ret; + ret = io->poll(io->context, T6050_PCIE_PORT0, 0x804, 1, 1, timeout_us); + if (ret) + return ret; + + ret = rmw(io, T6050_PCIE_PORT0, 0x800, 0, 0x100); + if (ret) + return ret; + return io->poll(io->context, T6050_PCIE_PORT0, 0x208, 4, 0, timeout_us); +} + +static int find_pcie_cap(const struct t6050_pcie_io *io, unsigned int *cap) +{ + u32 v; + int ret = io->read(io->context, T6050_PCIE_ECAM, 0x34, &v); + if (ret) + return ret; + unsigned int off = v & 0xff; + for (unsigned int i = 0; i < 48 && off; i++) { + if (off < 0x40 || (off & 3)) + return -EIO; + ret = io->read(io->context, T6050_PCIE_ECAM, off, &v); + if (ret) + return ret; + if ((v & 0xff) == 0x10) { + *cap = off; + return 0; + } + off = (v >> 8) & 0xff; + } + return -EIO; +} + +int t6050_pcie_configure_port0(const struct t6050_pcie_io *io, + unsigned int max_speed) +{ + static const char *const shadow[] = { "pcie-rc-gen3-shadow-tunables", + "pcie-rc-gen4-shadow-tunables", + "pcie-rc-gen5-shadow-tunables" }; + const u32 select[] = { 0, 1u << 24, 0 }; + unsigned int cap; + u32 v; + u16 v16; + int ret; + if (!io || !io->read || !io->write || !io->read16 || !io->write16 || + !io->apply_port_tunables || max_speed != 3) + return -EINVAL; + ret = find_pcie_cap(io, &cap); + if (ret) + return ret; + ret = rmw(io, T6050_PCIE_ECAM, 0x8bc, 0, 1); + if (ret) + return ret; + ret = io->apply_port_tunables(io->context, "pcie-rc-tunables", + T6050_PCIE_ECAM); + if (ret) + goto lock; + + for (unsigned int i = 0; i < 3; i++) { + ret = rmw(io, T6050_PCIE_ECAM, 0x890, 3u << 24, select[i]); + if (ret) + goto lock; + ret = io->apply_port_tunables(io->context, shadow[i], + T6050_PCIE_ECAM); + if (ret) + goto lock; + } + ret = rmw(io, T6050_PCIE_ECAM, 0x194, 1, 1); + if (ret) + goto lock; + ret = io->read(io->context, T6050_PCIE_ECAM, cap + 0x2c, &v); + if (ret) + goto lock; + if (!(v & (1u << max_speed))) { + ret = -EIO; + goto lock; + } + ret = io->read16(io->context, T6050_PCIE_ECAM, cap + 0x30, &v16); + if (ret) + goto lock; + ret = io->write16(io->context, T6050_PCIE_ECAM, cap + 0x30, + (v16 & ~0xf) | max_speed); + if (ret) + goto lock; + + ret = rmw(io, T6050_PCIE_ECAM, 0x710, 0x3fu << 16, 1u << 16); + if (ret) + goto lock; + ret = rmw(io, T6050_PCIE_ECAM, 0x80c, 0x1fu << 8, 1u << 8); + if (ret) + goto lock; + ret = rmw(io, T6050_PCIE_ECAM, cap + 0xc, 0x3fu << 4, 1u << 4); +lock: + +{ + int cleanup = rmw(io, T6050_PCIE_ECAM, 0x8bc, 1, 0); + if (!ret) + ret = cleanup; +} + if (ret) + return ret; + ret = rmw(io, T6050_PCIE_ECAM, cap + 0x10, 0, 0x400); + if (ret) + return ret; + ret = rmw(io, T6050_PCIE_ECAM, 0x80c, 0, 1u << 17); + if (ret) + return ret; + + return io->write(io->context, T6050_PCIE_PORT0_INTR2AXI, 0x80, 1); +} + +int t6050_pcie_train_port0(const struct t6050_pcie_io *io, + unsigned int timeout_us) +{ + int ret; + if (!io || !io->read || !io->write || !io->poll || !io->delay || + !io->endpoint_reset || !timeout_us) + return -EINVAL; + + ret = rmw(io, T6050_PCIE_PORT0_PHY, 0, 0, 1); + if (ret) + return ret; + ret = io->poll(io->context, T6050_PCIE_PORT0_PHY, 0, 4, 4, timeout_us); + if (ret) + return ret; + ret = rmw(io, T6050_PCIE_PORT0_PHY, 0, 0, 2); + if (ret) + return ret; + ret = io->poll(io->context, T6050_PCIE_PORT0_PHY, 0, 8, 8, timeout_us); + if (ret) + return ret; + ret = rmw(io, T6050_PCIE_PORT0_PHY, 0, 0x10, 0); + if (ret) + return ret; + io->delay(io->context, 1); + ret = rmw(io, T6050_PCIE_PORT0_PHY, 0, 0, 0x200); + if (ret) + return ret; + ret = rmw(io, T6050_PCIE_PORT0_PHY, 0, 0, 0x400); + if (ret) + return ret; + io->delay(io->context, 100); + ret = io->endpoint_reset(io->context, false); + if (ret) + return ret; + ret = rmw(io, T6050_PCIE_PORT0, 0x80, 0, 1); + if (ret) + return ret; + ret = io->poll(io->context, T6050_PCIE_PORT0, 0x208, 1, 1, timeout_us); + if (ret) + return ret; + io->delay(io->context, 100000); + return 0; +} diff --git a/drivers/pci/controller/apple-apif/t6050_pcie_sequence.h b/drivers/pci/controller/apple-apif/t6050_pcie_sequence.h new file mode 100644 index 0000000000000..74782754bc608 --- /dev/null +++ b/drivers/pci/controller/apple-apif/t6050_pcie_sequence.h @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: GPL-2.0-only +#ifndef T6050_PCIE_SEQUENCE_H +#define T6050_PCIE_SEQUENCE_H +#include + +struct t6050_pcie_io { + void *context; + int (*read)(void *, unsigned int adt_reg, unsigned int offset, u32 *); + int (*write)(void *, unsigned int adt_reg, unsigned int offset, u32); + int (*poll)(void *, unsigned int adt_reg, unsigned int offset, u32 mask, + u32 value, unsigned int timeout_us); + int (*apply_tunables)(void *, const char *property, + unsigned int adt_reg); + int (*apply_port_tunables)(void *, const char *property, + unsigned int adt_reg); + int (*read16)(void *, unsigned int adt_reg, unsigned int offset, u16 *); + int (*write16)(void *, unsigned int adt_reg, unsigned int offset, u16); + void (*delay)(void *, unsigned int usec); + + int (*endpoint_reset)(void *, bool asserted); +}; + +enum { + T6050_PCIE_ECAM = 0, + T6050_PCIE_PORT0 = 16, + T6050_PCIE_PORT0_PHY = 18, + T6050_PCIE_PORT0_INTR2AXI = 19, +}; + +int t6050_pcie_reset_port0(const struct t6050_pcie_io *io); +int t6050_pcie_enable_port0(const struct t6050_pcie_io *io, + unsigned int timeout_us); +int t6050_pcie_configure_port0(const struct t6050_pcie_io *io, + unsigned int max_speed); +int t6050_pcie_train_port0(const struct t6050_pcie_io *io, + unsigned int timeout_us); +int t6050_pcie_configure_msi_port0(const struct t6050_pcie_io *io, u64 doorbell, + unsigned int count); +#endif diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 27008e2ea5afc..d625dd5e778a8 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -3517,6 +3517,13 @@ void pci_lock_rescan_remove(void) } EXPORT_SYMBOL_GPL(pci_lock_rescan_remove); +/* Work cancelled by removal must not wait on the remover's own lock. */ +bool pci_trylock_rescan_remove(void) +{ + return mutex_trylock(&pci_rescan_remove_lock); +} +EXPORT_SYMBOL_GPL(pci_trylock_rescan_remove); + void pci_unlock_rescan_remove(void) { mutex_unlock(&pci_rescan_remove_lock); diff --git a/include/linux/pci.h b/include/linux/pci.h index 5cec6d838b3f7..c0d76d93a78f5 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1559,6 +1559,7 @@ void set_pcie_hotplug_bridge(struct pci_dev *pdev); /* Functions for PCI Hotplug drivers to use */ unsigned int pci_rescan_bus(struct pci_bus *bus); void pci_lock_rescan_remove(void); +bool pci_trylock_rescan_remove(void); void pci_unlock_rescan_remove(void); /* Vital Product Data routines */ diff --git a/tools/testing/selftests/aurora-apif/pcie.py b/tools/testing/selftests/aurora-apif/pcie.py new file mode 100755 index 0000000000000..05dd3ca27b222 --- /dev/null +++ b/tools/testing/selftests/aurora-apif/pcie.py @@ -0,0 +1,7 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0-only +"""Run the production PCIE fault harness.""" +from apif_mmio import run_tests, pcie_test + +if __name__ == "__main__": + run_tests([("pcie", pcie_test)])