Commit 6b0567da authored by Bjorn Helgaas's avatar Bjorn Helgaas

Merge branch 'pci/host/apple'

- Make of_phandle_args_to_fwspec() generally available (Marc Zyngier)

- Allow matching of interrupt-maps local to interrupt controller or PCI
  device (Marc Zyngier)

- Add Apple SoC (e.g., M1) PCIe host controller driver, which enables
  access to USB type-A, Ethernet, Wi-Fi, Bluetooth devices; these require
  additional drivers of their own (Alyssa Rosenzweig)

- Add apple INTx, per-port, and MSI interrupt support (Marc Zyngier)

- Configure apple Requester-ID-to-Stream-ID mapper for IOMMU (DART) support
  (Marc Zyngier)

* pci/host/apple:
  PCI: apple: Configure RID to SID mapper on device addition
  iommu/dart: Exclude MSI doorbell from PCIe device IOVA range
  PCI: apple: Implement MSI support
  PCI: apple: Add INTx and per-port interrupt support
  PCI: apple: Set up reference clocks when probing
  PCI: apple: Add initial hardware bring-up
  PCI: of: Allow matching of an interrupt-map local to a PCI device
  of/irq: Allow matching of an interrupt-map local to an interrupt controller
  irqdomain: Make of_phandle_args_to_fwspec() generally available
parents 27e76d06 468c8d52
......@@ -1280,6 +1280,13 @@ S: Maintained
F: Documentation/devicetree/bindings/iommu/apple,dart.yaml
F: drivers/iommu/apple-dart.c
APPLE PCIE CONTROLLER DRIVER
M: Alyssa Rosenzweig <alyssa@rosenzweig.io>
M: Marc Zyngier <maz@kernel.org>
L: linux-pci@vger.kernel.org
S: Maintained
F: drivers/pci/controller/pcie-apple.c
APPLE SMC DRIVER
M: Henrik Rydberg <rydberg@bitmath.org>
L: linux-hwmon@vger.kernel.org
......
......@@ -721,6 +721,31 @@ static int apple_dart_def_domain_type(struct device *dev)
return 0;
}
#ifndef CONFIG_PCIE_APPLE_MSI_DOORBELL_ADDR
/* Keep things compiling when CONFIG_PCI_APPLE isn't selected */
#define CONFIG_PCIE_APPLE_MSI_DOORBELL_ADDR 0
#endif
#define DOORBELL_ADDR (CONFIG_PCIE_APPLE_MSI_DOORBELL_ADDR & PAGE_MASK)
static void apple_dart_get_resv_regions(struct device *dev,
struct list_head *head)
{
if (IS_ENABLED(CONFIG_PCIE_APPLE) && dev_is_pci(dev)) {
struct iommu_resv_region *region;
int prot = IOMMU_WRITE | IOMMU_NOEXEC | IOMMU_MMIO;
region = iommu_alloc_resv_region(DOORBELL_ADDR,
PAGE_SIZE, prot,
IOMMU_RESV_MSI);
if (!region)
return;
list_add_tail(&region->list, head);
}
iommu_dma_get_resv_regions(dev, head);
}
static const struct iommu_ops apple_dart_iommu_ops = {
.domain_alloc = apple_dart_domain_alloc,
.domain_free = apple_dart_domain_free,
......@@ -737,6 +762,8 @@ static const struct iommu_ops apple_dart_iommu_ops = {
.device_group = apple_dart_device_group,
.of_xlate = apple_dart_of_xlate,
.def_domain_type = apple_dart_def_domain_type,
.get_resv_regions = apple_dart_get_resv_regions,
.put_resv_regions = generic_iommu_put_resv_regions,
.pgsize_bitmap = -1UL, /* Restricted during dart probe */
};
......
......@@ -156,10 +156,14 @@ int of_irq_parse_raw(const __be32 *addr, struct of_phandle_args *out_irq)
/* Now start the actual "proper" walk of the interrupt tree */
while (ipar != NULL) {
/* Now check if cursor is an interrupt-controller and if it is
* then we are done
/*
* Now check if cursor is an interrupt-controller and
* if it is then we are done, unless there is an
* interrupt-map which takes precedence.
*/
if (of_property_read_bool(ipar, "interrupt-controller")) {
imap = of_get_property(ipar, "interrupt-map", &imaplen);
if (imap == NULL &&
of_property_read_bool(ipar, "interrupt-controller")) {
pr_debug(" -> got it !\n");
return 0;
}
......@@ -173,8 +177,6 @@ int of_irq_parse_raw(const __be32 *addr, struct of_phandle_args *out_irq)
goto fail;
}
/* Now look for an interrupt-map */
imap = of_get_property(ipar, "interrupt-map", &imaplen);
/* No interrupt map, check for an interrupt parent */
if (imap == NULL) {
pr_debug(" -> no map, getting parent\n");
......@@ -255,6 +257,11 @@ int of_irq_parse_raw(const __be32 *addr, struct of_phandle_args *out_irq)
out_irq->args_count = intsize = newintsize;
addrsize = newaddrsize;
if (ipar == newpar) {
pr_debug("%pOF interrupt-map entry to self\n", ipar);
return 0;
}
skiplevel:
/* Iterate again with new parent */
out_irq->np = newpar;
......
......@@ -312,6 +312,24 @@ config PCIE_HISI_ERR
Say Y here if you want error handling support
for the PCIe controller's errors on HiSilicon HIP SoCs
config PCIE_APPLE_MSI_DOORBELL_ADDR
hex
default 0xfffff000
depends on PCIE_APPLE
config PCIE_APPLE
tristate "Apple PCIe controller"
depends on ARCH_APPLE || COMPILE_TEST
depends on OF
depends on PCI_MSI_IRQ_DOMAIN
select PCI_HOST_COMMON
help
Say Y here if you want to enable PCIe controller support on Apple
system-on-chips, like the Apple M1. This is required for the USB
type-A ports, Ethernet, Wi-Fi, and Bluetooth.
If unsure, say Y if you have an Apple Silicon system.
source "drivers/pci/controller/dwc/Kconfig"
source "drivers/pci/controller/mobiveil/Kconfig"
source "drivers/pci/controller/cadence/Kconfig"
......
......@@ -37,6 +37,7 @@ obj-$(CONFIG_VMD) += vmd.o
obj-$(CONFIG_PCIE_BRCMSTB) += pcie-brcmstb.o
obj-$(CONFIG_PCI_LOONGSON) += pci-loongson.o
obj-$(CONFIG_PCIE_HISI_ERR) += pcie-hisi-error.o
obj-$(CONFIG_PCIE_APPLE) += pcie-apple.o
# pcie-hisi.o quirks are needed even without CONFIG_PCIE_DW
obj-y += dwc/
obj-y += mobiveil/
......
This diff is collapsed.
......@@ -423,7 +423,7 @@ static int devm_of_pci_get_host_bridge_resources(struct device *dev,
*/
static int of_irq_parse_pci(const struct pci_dev *pdev, struct of_phandle_args *out_irq)
{
struct device_node *dn, *ppnode;
struct device_node *dn, *ppnode = NULL;
struct pci_dev *ppdev;
__be32 laddr[3];
u8 pin;
......@@ -452,8 +452,14 @@ static int of_irq_parse_pci(const struct pci_dev *pdev, struct of_phandle_args *
if (pin == 0)
return -ENODEV;
/* Local interrupt-map in the device node? Use it! */
if (of_get_property(dn, "interrupt-map", NULL)) {
pin = pci_swizzle_interrupt_pin(pdev, pin);
ppnode = dn;
}
/* Now we walk up the PCI tree */
for (;;) {
while (!ppnode) {
/* Get the pci_dev of our parent */
ppdev = pdev->bus->self;
......
......@@ -64,6 +64,10 @@ struct irq_fwspec {
u32 param[IRQ_DOMAIN_IRQ_SPEC_PARAMS];
};
/* Conversion function from of_phandle_args fields to fwspec */
void of_phandle_args_to_fwspec(struct device_node *np, const u32 *args,
unsigned int count, struct irq_fwspec *fwspec);
/*
* Should several domains have the same device node, but serve
* different purposes (for example one domain is for PCI/MSI, and the
......
......@@ -744,9 +744,8 @@ static int irq_domain_translate(struct irq_domain *d,
return 0;
}
static void of_phandle_args_to_fwspec(struct device_node *np, const u32 *args,
unsigned int count,
struct irq_fwspec *fwspec)
void of_phandle_args_to_fwspec(struct device_node *np, const u32 *args,
unsigned int count, struct irq_fwspec *fwspec)
{
int i;
......@@ -756,6 +755,7 @@ static void of_phandle_args_to_fwspec(struct device_node *np, const u32 *args,
for (i = 0; i < count; i++)
fwspec->param[i] = args[i];
}
EXPORT_SYMBOL_GPL(of_phandle_args_to_fwspec);
unsigned int irq_create_fwspec_mapping(struct irq_fwspec *fwspec)
{
......@@ -1502,6 +1502,7 @@ int __irq_domain_alloc_irqs(struct irq_domain *domain, int irq_base,
irq_free_descs(virq, nr_irqs);
return ret;
}
EXPORT_SYMBOL_GPL(__irq_domain_alloc_irqs);
/* The irq_data was moved, fix the revmap to refer to the new location */
static void irq_domain_fix_revmap(struct irq_data *d)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment