This adds a PCIe driver for the internal bus on the Apple M1 (and
presumably other Apple system-on-chips). It's based on the work of Marc
Zyngier, Mark Kettenis, and Stan Skowronek (Corellium). In conjunction
with a pinctrl driver, this enables the USB type-A ports and the
Ethernet port. It also paves the way for Wi-Fi and Bluetooth, but that
requires further work.
For the largest change since v1 of the series-- this now uses Mark
Kettenis's device tree bindings for PCIe. This series contains Mark's
patches (currently under discussion on the LKML) adding the device tree
nodes required for PCIe. I have made minor modifications to Mark's
original patches to get everything working under Linux:
* In the bindings themselves, I've increased the maximum number of
interrupts to accommodate the full set.
* In the PCIe node, I've added the full set of interrupts.
* I've added the PCIe DART nodes (IOMMUs) and the corresponding
iommu-map(-mask) properties already covered in the bindings.
* I've tweaked the sizes of the `reg` blocks. Otherwise I got a page
fault early on.
I've collected the patches required to test on this branch:
https://github.com/mu-one/linux/commits/pcie-v2
This branch is based on linux-next and contains a GPIO (pinctrl) driver,
a clock gate driver, additional device tree nodes, and this series. The
type-A ports and Ethernet should work out-of-the-box on that tree,
provided the kernel is booted through m1n1. This improves on Maz's
initial PCIe driver, which required U-Boot to function.
I've started using Linux on M1 as my workstation for Panfrost
development, so this should have 40 hours of testing by this time next
week.
== Project Blurb ==
Asahi Linux is an open community project dedicated to developing and
maintaining mainline support for Apple Silicon on Linux. Feel free to
drop by #asahi and #asahi-dev on OFTC to chat with us, or check
our website for more information on the project:
== Changes ==
Changes for v2:
- Cherrypicked Mark's device tree bindings and switched to using them.
- Split up the PCI driver patch into 3.
- Large numbers of minor changes to the driver better match upstream
quality standards (using more helper functions, etc.)
Alyssa Rosenzweig (3):
PCI: apple: Add initial hardware bring-up
PCI: apple: Set up reference clocks when probing
PCI: apple: Add MSI handling
Mark Kettenis (3):
dt-bindings: pci: Add DT bindings for apple,pcie
arm64: apple: Add pinctrl nodes
arm64: apple: Add PCIe node
.../devicetree/bindings/pci/apple,pcie.yaml | 166 +++++++
MAINTAINERS | 7 +
arch/arm64/boot/dts/apple/t8103.dtsi | 207 ++++++++
drivers/pci/controller/Kconfig | 12 +
drivers/pci/controller/Makefile | 1 +
drivers/pci/controller/pcie-apple.c | 448 ++++++++++++++++++
6 files changed, 841 insertions(+)
create mode 100644 Documentation/devicetree/bindings/pci/apple,pcie.yaml
create mode 100644 drivers/pci/controller/pcie-apple.c
--
2.30.2
From: Mark Kettenis <[email protected]>
The Apple PCIe host controller is a PCIe host controller with
multiple root ports present in Apple ARM SoC platforms, including
various iPhone and iPad devices and the "Apple Silicon" Macs.
Signed-off-by: Mark Kettenis <[email protected]>
Signed-off-by: Alyssa Rosenzweig <[email protected]>
---
.../devicetree/bindings/pci/apple,pcie.yaml | 166 ++++++++++++++++++
MAINTAINERS | 1 +
2 files changed, 167 insertions(+)
create mode 100644 Documentation/devicetree/bindings/pci/apple,pcie.yaml
diff --git a/Documentation/devicetree/bindings/pci/apple,pcie.yaml b/Documentation/devicetree/bindings/pci/apple,pcie.yaml
new file mode 100644
index 000000000000..054f6f069833
--- /dev/null
+++ b/Documentation/devicetree/bindings/pci/apple,pcie.yaml
@@ -0,0 +1,166 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/pci/apple,pcie.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Apple PCIe host controller
+
+maintainers:
+ - Mark Kettenis <[email protected]>
+
+description: |
+ The Apple PCIe host controller is a PCIe host controller with
+ multiple root ports present in Apple ARM SoC platforms, including
+ various iPhone and iPad devices and the "Apple Silicon" Macs.
+ The controller incorporates Synopsys DesigWare PCIe logic to
+ implements its root ports. But the ATU found on most DesignWare
+ PCIe host bridges is absent.
+ All root ports share a single ECAM space, but separate GPIOs are
+ used to take the PCI devices on those ports out of reset. Therefore
+ the standard "reset-gpio" and "max-link-speed" properties appear on
+ the child nodes that represent the PCI bridges that correspond to
+ the individual root ports.
+ MSIs are handled by the PCIe controller and translated into regular
+ interrupts. A range of 32 MSIs is provided. These 32 MSIs can be
+ distributed over the root ports as the OS sees fit by programming
+ the PCIe controller's port registers.
+
+allOf:
+ - $ref: /schemas/pci/pci-bus.yaml#
+
+properties:
+ compatible:
+ items:
+ - const: apple,t8103-pcie
+ - const: apple,pcie
+
+ reg:
+ minItems: 3
+ maxItems: 5
+
+ reg-names:
+ minItems: 3
+ maxItems: 5
+ items:
+ - const: config
+ - const: rc
+ - const: port0
+ - const: port1
+ - const: port2
+
+ ranges:
+ minItems: 2
+ maxItems: 2
+
+ interrupts:
+ description:
+ Interrupt specifiers.
+ minItems: 1
+ maxItems: 35
+
+ msi-controller: true
+ msi-parent: true
+
+ msi-ranges:
+ description:
+ A list of pairs <intid span>, where "intid" is the first
+ interrupt number that can be used as an MSI, and "span" the size
+ of that range.
+ $ref: /schemas/types.yaml#/definitions/uint32-matrix
+ items:
+ minItems: 2
+ maxItems: 2
+
+ iommu-map: true
+ iommu-map-mask: true
+
+required:
+ - compatible
+ - reg
+ - reg-names
+ - bus-range
+ - interrupts
+ - msi-controller
+ - msi-parent
+ - msi-ranges
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/apple-aic.h>
+
+ soc {
+ #address-cells = <2>;
+ #size-cells = <2>;
+
+ pcie0: pcie@690000000 {
+ compatible = "apple,t8103-pcie", "apple,pcie";
+ device_type = "pci";
+
+ reg = <0x6 0x90000000 0x0 0x1000000>,
+ <0x6 0x80000000 0x0 0x4000>,
+ <0x6 0x81000000 0x0 0x8000>,
+ <0x6 0x82000000 0x0 0x8000>,
+ <0x6 0x83000000 0x0 0x8000>;
+ reg-names = "config", "rc", "port0", "port1", "port2";
+
+ interrupt-parent = <&aic>;
+ interrupts = <AIC_IRQ 695 IRQ_TYPE_LEVEL_HIGH>,
+ <AIC_IRQ 698 IRQ_TYPE_LEVEL_HIGH>,
+ <AIC_IRQ 701 IRQ_TYPE_LEVEL_HIGH>;
+
+ msi-controller;
+ msi-parent = <&pcie0>;
+ msi-ranges = <704 32>;
+
+ iommu-map = <0x100 &dart0 1 1>,
+ <0x200 &dart1 1 1>,
+ <0x300 &dart2 1 1>;
+ iommu-map-mask = <0xff00>;
+
+ bus-range = <0 3>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+ ranges = <0x43000000 0x6 0xa0000000 0x6 0xa0000000 0x0 0x20000000>,
+ <0x02000000 0x0 0xc0000000 0x6 0xc0000000 0x0 0x40000000>;
+
+ clocks = <&pcie_core_clk>, <&pcie_aux_clk>, <&pcie_ref_clk>;
+ pinctrl-0 = <&pcie_pins>;
+ pinctrl-names = "default";
+
+ pci@0,0 {
+ device_type = "pci";
+ reg = <0x0 0x0 0x0 0x0 0x0>;
+ reset-gpios = <&pinctrl_ap 152 0>;
+ max-link-speed = <2>;
+
+ #address-cells = <3>;
+ #size-cells = <2>;
+ ranges;
+ };
+
+ pci@1,0 {
+ device_type = "pci";
+ reg = <0x800 0x0 0x0 0x0 0x0>;
+ reset-gpios = <&pinctrl_ap 153 0>;
+ max-link-speed = <2>;
+
+ #address-cells = <3>;
+ #size-cells = <2>;
+ ranges;
+ };
+
+ pci@2,0 {
+ device_type = "pci";
+ reg = <0x1000 0x0 0x0 0x0 0x0>;
+ reset-gpios = <&pinctrl_ap 33 0>;
+ max-link-speed = <1>;
+
+ #address-cells = <3>;
+ #size-cells = <2>;
+ ranges;
+ };
+ };
+ };
diff --git a/MAINTAINERS b/MAINTAINERS
index b63403793c81..a5687cf6f925 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1701,6 +1701,7 @@ C: irc://irc.oftc.net/asahi-dev
T: git https://github.com/AsahiLinux/linux.git
F: Documentation/devicetree/bindings/arm/apple.yaml
F: Documentation/devicetree/bindings/interrupt-controller/apple,aic.yaml
+F: Documentation/devicetree/bindings/pci/apple,pcie.yaml
F: Documentation/devicetree/bindings/pinctrl/apple,pinctrl.yaml
F: arch/arm64/boot/dts/apple/
F: drivers/irqchip/irq-apple-aic.c
--
2.30.2
Add MSI handling to the Apple PCIe driver. As the hardware is ECAM
compliant, this code is the only hardware-specific piece required
after probing. This is sufficient to bring up Ethernet and the USB type
A ports.
Co-developed-by: Marc Zyngier <[email protected]>
Signed-off-by: Marc Zyngier <[email protected]>
Signed-off-by: Alyssa Rosenzweig <[email protected]>
---
drivers/pci/controller/pcie-apple.c | 152 +++++++++++++++++++++++++++-
1 file changed, 151 insertions(+), 1 deletion(-)
diff --git a/drivers/pci/controller/pcie-apple.c b/drivers/pci/controller/pcie-apple.c
index 4ab767cf841b..04ebb9b956ae 100644
--- a/drivers/pci/controller/pcie-apple.c
+++ b/drivers/pci/controller/pcie-apple.c
@@ -119,6 +119,10 @@ struct apple_pcie {
struct mutex lock;
struct device *dev;
void __iomem *rc;
+ struct irq_domain *domain;
+ unsigned long *bitmap;
+ u32 msi_base;
+ u32 nvecs;
};
static inline void rmwl(u32 clr, u32 set, void __iomem *addr)
@@ -126,6 +130,105 @@ static inline void rmwl(u32 clr, u32 set, void __iomem *addr)
writel_relaxed((readl_relaxed(addr) & ~clr) | set, addr);
}
+static void apple_msi_top_irq_mask(struct irq_data *d)
+{
+ pci_msi_mask_irq(d);
+ irq_chip_mask_parent(d);
+}
+
+static void apple_msi_top_irq_unmask(struct irq_data *d)
+{
+ pci_msi_unmask_irq(d);
+ irq_chip_unmask_parent(d);
+}
+
+static struct irq_chip apple_msi_top_chip = {
+ .name = "PCIe MSI",
+ .irq_mask = apple_msi_top_irq_mask,
+ .irq_unmask = apple_msi_top_irq_unmask,
+ .irq_eoi = irq_chip_eoi_parent,
+ .irq_set_affinity = irq_chip_set_affinity_parent,
+ .irq_set_type = irq_chip_set_type_parent,
+};
+
+static void apple_msi_compose_msg(struct irq_data *data, struct msi_msg *msg)
+{
+ msg->address_hi = upper_32_bits(DOORBELL_ADDR);
+ msg->address_lo = lower_32_bits(DOORBELL_ADDR);
+ msg->data = data->hwirq;
+}
+
+static struct irq_chip apple_msi_bottom_chip = {
+ .name = "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 = apple_msi_compose_msg,
+};
+
+static int apple_msi_domain_alloc(struct irq_domain *domain, unsigned int virq,
+ unsigned int nr_irqs, void *args)
+{
+ struct apple_pcie *pcie = domain->host_data;
+ struct irq_fwspec fwspec;
+ unsigned int i;
+ int ret, hwirq;
+
+ 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.fwnode = domain->parent->fwnode;
+ fwspec.param_count = 3;
+ fwspec.param[0] = 0;
+ fwspec.param[1] = hwirq + pcie->msi_base;
+ fwspec.param[2] = IRQ_TYPE_EDGE_RISING;
+
+ ret = irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, &fwspec);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < nr_irqs; i++) {
+ irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i,
+ &apple_msi_bottom_chip,
+ domain->host_data);
+ }
+
+ return 0;
+}
+
+static void apple_msi_domain_free(struct irq_domain *domain, unsigned int virq,
+ unsigned int nr_irqs)
+{
+ struct irq_data *d = irq_domain_get_irq_data(domain, virq);
+ struct apple_pcie *pcie = domain->host_data;
+
+ mutex_lock(&pcie->lock);
+
+ bitmap_release_region(pcie->bitmap, d->hwirq, order_base_2(nr_irqs));
+
+ mutex_unlock(&pcie->lock);
+}
+
+static const struct irq_domain_ops apple_msi_domain_ops = {
+ .alloc = apple_msi_domain_alloc,
+ .free = apple_msi_domain_free,
+};
+
+static struct msi_domain_info apple_msi_info = {
+ .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
+ MSI_FLAG_MULTI_PCI_MSI | MSI_FLAG_PCI_MSIX),
+ .chip = &apple_msi_top_chip,
+};
+
static int apple_pcie_setup_refclk(void __iomem *rc,
void __iomem *port,
unsigned int idx)
@@ -225,6 +328,53 @@ static int apple_pcie_setup_port(struct apple_pcie *pcie,
return 0;
}
+static int apple_msi_init(struct apple_pcie *pcie)
+{
+ struct fwnode_handle *fwnode = dev_fwnode(pcie->dev);
+ struct device_node *parent_intc;
+ struct irq_domain *parent;
+ int ret;
+
+ ret = of_property_read_u32_index(to_of_node(fwnode), "msi-ranges",
+ 0, &pcie->msi_base);
+ if (ret)
+ return ret;
+
+ ret = of_property_read_u32_index(to_of_node(fwnode), "msi-ranges",
+ 1, &pcie->nvecs);
+ if (ret)
+ return ret;
+
+ pcie->bitmap = devm_bitmap_zalloc(pcie->dev, pcie->nvecs, GFP_KERNEL);
+ if (!pcie->bitmap)
+ return -ENOMEM;
+
+ parent_intc = of_irq_find_parent(to_of_node(fwnode));
+ parent = irq_find_host(parent_intc);
+ if (!parent_intc || !parent) {
+ dev_err(pcie->dev, "failed to find parent domain\n");
+ return -ENXIO;
+ }
+
+ parent = irq_domain_create_hierarchy(parent, 0, pcie->nvecs, fwnode,
+ &apple_msi_domain_ops, pcie);
+ if (!parent) {
+ dev_err(pcie->dev, "failed to create IRQ domain\n");
+ return -ENOMEM;
+ }
+ irq_domain_update_bus_token(parent, DOMAIN_BUS_NEXUS);
+
+ pcie->domain = pci_msi_create_irq_domain(fwnode, &apple_msi_info,
+ parent);
+ if (!pcie->domain) {
+ dev_err(pcie->dev, "failed to create MSI domain\n");
+ irq_domain_remove(parent);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
static int apple_m1_pci_init(struct pci_config_window *cfg)
{
struct device *dev = cfg->parent;
@@ -267,7 +417,7 @@ static int apple_m1_pci_init(struct pci_config_window *cfg)
++i;
}
- return 0;
+ return apple_msi_init(pcie);
}
static const struct pci_ecam_ops apple_m1_cfg_ecam_ops = {
--
2.30.2
Add a minimal driver to bring up the PCIe bus on Apple system-on-chips,
particularly the Apple M1. This driver exposes the internal bus used for
the USB type-A ports, Ethernet, Wi-Fi, and Bluetooth. Bringing up the
radios requires additional drivers beyond what's necessary for PCIe
itself.
In this patch, a minimal driver is added that brings up the PCIe bus on
probe. This logic is derived from Corellium's driver via Mark Kettenis's
U-Boot patches.
Co-developed-by: Stan Skowronek <[email protected]>
Signed-off-by: Stan Skowronek <[email protected]>
Signed-off-by: Alyssa Rosenzweig <[email protected]>
---
MAINTAINERS | 6 +
drivers/pci/controller/Kconfig | 12 ++
drivers/pci/controller/Makefile | 1 +
drivers/pci/controller/pcie-apple.c | 251 ++++++++++++++++++++++++++++
4 files changed, 270 insertions(+)
create mode 100644 drivers/pci/controller/pcie-apple.c
diff --git a/MAINTAINERS b/MAINTAINERS
index a5687cf6f925..47cb3d320c56 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1269,6 +1269,12 @@ S: Maintained
F: Documentation/devicetree/bindings/iommu/apple,dart.yaml
F: drivers/iommu/apple-dart.c
+APPLE PCIE CONTROLLER DRIVER
+M: Alyssa Rosenzweig <[email protected]>
+L: [email protected]
+S: Maintained
+F: drivers/pci/controller/pcie-apple.c
+
APPLE SMC DRIVER
M: Henrik Rydberg <[email protected]>
L: [email protected]
diff --git a/drivers/pci/controller/Kconfig b/drivers/pci/controller/Kconfig
index 326f7d13024f..814833a8120d 100644
--- a/drivers/pci/controller/Kconfig
+++ b/drivers/pci/controller/Kconfig
@@ -312,6 +312,18 @@ 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
+ tristate "Apple PCIe controller"
+ depends on ARCH_APPLE || COMPILE_TEST
+ depends on OF
+ depends on PCI_MSI_IRQ_DOMAIN
+ 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"
diff --git a/drivers/pci/controller/Makefile b/drivers/pci/controller/Makefile
index aaf30b3dcc14..f9d40bad932c 100644
--- a/drivers/pci/controller/Makefile
+++ b/drivers/pci/controller/Makefile
@@ -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/
diff --git a/drivers/pci/controller/pcie-apple.c b/drivers/pci/controller/pcie-apple.c
new file mode 100644
index 000000000000..a1efcc3373ea
--- /dev/null
+++ b/drivers/pci/controller/pcie-apple.c
@@ -0,0 +1,251 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PCIe host bridge driver for Apple system-on-chips.
+ *
+ * The HW is ECAM compliant, so once the controller is initialized, the driver
+ * mostly only needs MSI handling. Initialization requires enabling power and
+ * clocks, along with a number of register pokes.
+ *
+ * Copyright (C) 2021 Alyssa Rosenzweig <[email protected]>
+ * Copyright (C) 2021 Google LLC
+ * Copyright (C) 2021 Corellium LLC
+ * Copyright (C) 2021 Mark Kettenis <[email protected]>
+ *
+ * Author: Alyssa Rosenzweig <[email protected]>
+ * Author: Marc Zyngier <[email protected]>
+ */
+
+#include <linux/kernel.h>
+#include <linux/irqdomain.h>
+#include <linux/module.h>
+#include <linux/msi.h>
+#include <linux/of_irq.h>
+#include <linux/pci-ecam.h>
+#include <linux/iopoll.h>
+#include <linux/gpio/consumer.h>
+
+#define CORE_RC_PHYIF_CTL 0x00024
+#define CORE_RC_PHYIF_CTL_RUN BIT(0)
+#define CORE_RC_PHYIF_STAT 0x00028
+#define CORE_RC_PHYIF_STAT_REFCLK BIT(4)
+#define CORE_RC_CTL 0x00050
+#define CORE_RC_CTL_RUN BIT(0)
+#define CORE_RC_STAT 0x00058
+#define CORE_RC_STAT_READY BIT(0)
+#define CORE_FABRIC_STAT 0x04000
+#define CORE_FABRIC_STAT_MASK 0x001F001F
+#define CORE_LANE_CFG(port) (0x84000 + 0x4000 * (port))
+#define CORE_LANE_CFG_REFCLK0REQ BIT(0)
+#define CORE_LANE_CFG_REFCLK1 BIT(1)
+#define CORE_LANE_CFG_REFCLK0ACK BIT(2)
+#define CORE_LANE_CFG_REFCLKEN (BIT(9) | BIT(10))
+#define CORE_LANE_CTL(port) (0x84004 + 0x4000 * (port))
+#define CORE_LANE_CTL_CFGACC BIT(15)
+
+#define PORT_LTSSMCTL 0x00080
+#define PORT_LTSSMCTL_START BIT(0)
+#define PORT_INTSTAT 0x00100
+#define PORT_INT_TUNNEL_ERR BIT(31)
+#define PORT_INT_CPL_TIMEOUT BIT(23)
+#define PORT_INT_RID2SID_MAPERR BIT(22)
+#define PORT_INT_CPL_ABORT BIT(21)
+#define PORT_INT_MSI_BAD_DATA BIT(19)
+#define PORT_INT_MSI_ERR BIT(18)
+#define PORT_INT_REQADDR_GT32 BIT(17)
+#define PORT_INT_AF_TIMEOUT BIT(15)
+#define PORT_INT_LINK_DOWN BIT(14)
+#define PORT_INT_LINK_UP BIT(12)
+#define PORT_INT_LINK_BWMGMT BIT(11)
+#define PORT_INT_AER_MASK (15 << 4)
+#define PORT_INT_PORT_ERR BIT(4)
+#define PORT_INT_INTx(i) BIT(i)
+#define PORT_INT_INTxALL 15
+#define PORT_INTMSK 0x00104
+#define PORT_INTMSKSET 0x00108
+#define PORT_INTMSKCLR 0x0010c
+#define PORT_MSICFG 0x00124
+#define PORT_MSICFG_EN BIT(0)
+#define PORT_MSICFG_L2MSINUM_SHIFT 4
+#define PORT_MSIBASE 0x00128
+#define PORT_MSIBASE_1_SHIFT 16
+#define PORT_MSIADDR 0x00168
+#define PORT_LINKSTS 0x00208
+#define PORT_LINKSTS_UP BIT(0)
+#define PORT_LINKSTS_BUSY BIT(2)
+#define PORT_LINKCMDSTS 0x00210
+#define PORT_OUTS_NPREQS 0x00284
+#define PORT_OUTS_NPREQS_REQ BIT(24)
+#define PORT_OUTS_NPREQS_CPL BIT(16)
+#define PORT_RXWR_FIFO 0x00288
+#define PORT_RXWR_FIFO_HDR GENMASK(15, 10)
+#define PORT_RXWR_FIFO_DATA GENMASK(9, 0)
+#define PORT_RXRD_FIFO 0x0028C
+#define PORT_RXRD_FIFO_REQ GENMASK(6, 0)
+#define PORT_OUTS_CPLS 0x00290
+#define PORT_OUTS_CPLS_SHRD GENMASK(14, 8)
+#define PORT_OUTS_CPLS_WAIT GENMASK(6, 0)
+#define PORT_APPCLK 0x00800
+#define PORT_APPCLK_EN BIT(0)
+#define PORT_APPCLK_CGDIS BIT(8)
+#define PORT_STATUS 0x00804
+#define PORT_STATUS_READY BIT(0)
+#define PORT_REFCLK 0x00810
+#define PORT_REFCLK_EN BIT(0)
+#define PORT_REFCLK_CGDIS BIT(8)
+#define PORT_PERST 0x00814
+#define PORT_PERST_OFF BIT(0)
+#define PORT_RID2SID(i16) (0x00828 + 4 * (i16))
+#define PORT_RID2SID_VALID BIT(31)
+#define PORT_RID2SID_SID_SHIFT 16
+#define PORT_RID2SID_BUS_SHIFT 8
+#define PORT_RID2SID_DEV_SHIFT 3
+#define PORT_RID2SID_FUNC_SHIFT 0
+#define PORT_OUTS_PREQS_HDR 0x00980
+#define PORT_OUTS_PREQS_HDR_MASK GENMASK(9, 0)
+#define PORT_OUTS_PREQS_DATA 0x00984
+#define PORT_OUTS_PREQS_DATA_MASK GENMASK(15, 0)
+#define PORT_TUNCTRL 0x00988
+#define PORT_TUNCTRL_PERST_ON BIT(0)
+#define PORT_TUNCTRL_PERST_ACK_REQ BIT(1)
+#define PORT_TUNSTAT 0x0098c
+#define PORT_TUNSTAT_PERST_ON BIT(0)
+#define PORT_TUNSTAT_PERST_ACK_PEND BIT(1)
+#define PORT_PREFMEM_ENABLE 0x00994
+
+/* The doorbell address is "well known" */
+#define DOORBELL_ADDR 0xfffff000
+
+struct apple_pcie {
+ struct mutex lock;
+ struct device *dev;
+ void __iomem *rc;
+};
+
+static inline void rmwl(u32 clr, u32 set, void __iomem *addr)
+{
+ writel_relaxed((readl_relaxed(addr) & ~clr) | set, addr);
+}
+
+static int apple_pcie_setup_port(struct apple_pcie *pcie,
+ struct gpio_desc *reset,
+ unsigned int i)
+{
+ struct platform_device *platform = to_platform_device(pcie->dev);
+ void __iomem *port;
+ uint32_t stat;
+ int ret;
+
+ port = devm_platform_ioremap_resource(platform, i + 2);
+
+ if (IS_ERR(port))
+ return -ENODEV;
+
+ /* Skip setup if the link was already enabled by the bootloader */
+ if (readl_relaxed(port + PORT_LINKSTS) & PORT_LINKSTS_UP)
+ return 0;
+
+ rmwl(0, PORT_PERST_OFF, port + PORT_PERST);
+ gpiod_set_value(reset, 1);
+
+ ret = readl_relaxed_poll_timeout(port + PORT_STATUS, stat,
+ stat & PORT_STATUS_READY, 100, 250000);
+ if (ret < 0) {
+ dev_err(pcie->dev, "port %u ready wait timeout\n", i);
+ return ret;
+ }
+
+ /* Configure MSIs */
+ writel_relaxed(DOORBELL_ADDR, port + PORT_MSIADDR);
+ writel_relaxed(0, port + PORT_MSIBASE);
+
+ /* Enable 32 MSIs */
+ writel_relaxed((5 << PORT_MSICFG_L2MSINUM_SHIFT) | PORT_MSICFG_EN,
+ port + PORT_MSICFG);
+
+ /* Enable link interrupts */
+ writel_relaxed(0xfb512fff, port + PORT_INTMSKSET);
+
+ writel_relaxed(PORT_INT_LINK_UP | PORT_INT_LINK_DOWN |
+ PORT_INT_AF_TIMEOUT | PORT_INT_REQADDR_GT32 |
+ PORT_INT_MSI_ERR | PORT_INT_MSI_BAD_DATA |
+ PORT_INT_CPL_ABORT | PORT_INT_CPL_TIMEOUT | (1 << 26),
+ port + PORT_INTSTAT);
+
+ /* Flush writes and enable the link */
+ dma_wmb();
+ writel_relaxed(PORT_LTSSMCTL_START, port + PORT_LTSSMCTL);
+
+ return 0;
+}
+
+static int apple_m1_pci_init(struct pci_config_window *cfg)
+{
+ struct device *dev = cfg->parent;
+ struct platform_device *platform = to_platform_device(dev);
+
+ struct apple_pcie *pcie;
+ struct device_node *of_port;
+ int ret, i;
+
+ pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL);
+ if (!pcie)
+ return -ENOMEM;
+
+ pcie->dev = dev;
+
+ mutex_init(&pcie->lock);
+
+ pcie->rc = devm_platform_ioremap_resource(platform, 1);
+
+ if (IS_ERR(pcie->rc))
+ return -ENODEV;
+
+ i = 0;
+
+ for_each_child_of_node(dev->of_node, of_port) {
+ struct gpio_desc *reset;
+
+ reset = gpiod_get_from_of_node(of_port, "reset-gpios", 0,
+ GPIOD_OUT_LOW, "#PERST");
+ if (IS_ERR(reset))
+ return PTR_ERR(reset);
+
+ ret = apple_pcie_setup_port(pcie, reset, i);
+
+ if (ret) {
+ dev_err(pcie->dev, "Port %u setup fail: %d\n", i, ret);
+ return ret;
+ }
+
+ ++i;
+ }
+
+ return 0;
+}
+
+static const struct pci_ecam_ops apple_m1_cfg_ecam_ops = {
+ .init = apple_m1_pci_init,
+ .pci_ops = {
+ .map_bus = pci_ecam_map_bus,
+ .read = pci_generic_config_read,
+ .write = pci_generic_config_write,
+ }
+};
+
+static const struct of_device_id apple_pci_of_match[] = {
+ { .compatible = "apple,pcie", .data = &apple_m1_cfg_ecam_ops },
+ { }
+};
+MODULE_DEVICE_TABLE(of, apple_pci_of_match);
+
+static struct platform_driver apple_pci_driver = {
+ .driver = {
+ .name = "pcie-apple",
+ .of_match_table = apple_pci_of_match,
+ },
+ .probe = pci_host_common_probe,
+ .remove = pci_host_common_remove,
+};
+module_platform_driver(apple_pci_driver);
+
+MODULE_LICENSE("GPL v2");
--
2.30.2
Apple's PCIe controller requires clocks to be configured in order to
bring up the hardware. Add the register pokes required to do so. Adapted
from Corellium's driver via Mark Kettenis's U-Boot patches.
Co-developed-by: Stan Skowronek <[email protected]>
Signed-off-by: Stan Skowronek <[email protected]>
Signed-off-by: Alyssa Rosenzweig <[email protected]>
---
drivers/pci/controller/pcie-apple.c | 47 +++++++++++++++++++++++++++++
1 file changed, 47 insertions(+)
diff --git a/drivers/pci/controller/pcie-apple.c b/drivers/pci/controller/pcie-apple.c
index a1efcc3373ea..4ab767cf841b 100644
--- a/drivers/pci/controller/pcie-apple.c
+++ b/drivers/pci/controller/pcie-apple.c
@@ -126,6 +126,47 @@ static inline void rmwl(u32 clr, u32 set, void __iomem *addr)
writel_relaxed((readl_relaxed(addr) & ~clr) | set, addr);
}
+static int apple_pcie_setup_refclk(void __iomem *rc,
+ void __iomem *port,
+ unsigned int idx)
+{
+ u32 stat;
+ int res;
+
+ res = readl_relaxed_poll_timeout(rc + CORE_RC_PHYIF_STAT, stat,
+ stat & CORE_RC_PHYIF_STAT_REFCLK,
+ 100, 50000);
+ if (res < 0)
+ return res;
+
+ rmwl(0, CORE_LANE_CTL_CFGACC, rc + CORE_LANE_CTL(idx));
+ rmwl(0, CORE_LANE_CFG_REFCLK0REQ, rc + CORE_LANE_CFG(idx));
+
+ res = readl_relaxed_poll_timeout(rc + CORE_LANE_CFG(idx), stat,
+ stat & CORE_LANE_CFG_REFCLK0ACK,
+ 100, 50000);
+ if (res < 0)
+ return res;
+
+ rmwl(0, CORE_LANE_CFG_REFCLK1, rc + CORE_LANE_CFG(idx));
+ res = readl_relaxed_poll_timeout(rc + CORE_LANE_CFG(idx), stat,
+ stat & CORE_LANE_CFG_REFCLK1,
+ 100, 50000);
+
+ if (res < 0)
+ return res;
+
+ rmwl(CORE_LANE_CTL_CFGACC, 0, rc + CORE_LANE_CTL(idx));
+
+ /* Flush writes before enabling the clocks */
+ dma_wmb();
+
+ rmwl(0, CORE_LANE_CFG_REFCLKEN, rc + CORE_LANE_CFG(idx));
+ rmwl(0, PORT_REFCLK_EN, port + PORT_REFCLK);
+
+ return 0;
+}
+
static int apple_pcie_setup_port(struct apple_pcie *pcie,
struct gpio_desc *reset,
unsigned int i)
@@ -144,6 +185,12 @@ static int apple_pcie_setup_port(struct apple_pcie *pcie,
if (readl_relaxed(port + PORT_LINKSTS) & PORT_LINKSTS_UP)
return 0;
+ rmwl(0, PORT_APPCLK_EN, port + PORT_APPCLK);
+
+ ret = apple_pcie_setup_refclk(pcie->rc, port, i);
+ if (ret < 0)
+ return ret;
+
rmwl(0, PORT_PERST_OFF, port + PORT_PERST);
gpiod_set_value(reset, 1);
--
2.30.2
From: Mark Kettenis <[email protected]>
Add pinctrl nodes corresponding to the gpio,t8101 nodes in the
Apple device tree for the Mac mini (M1, 2020).
Clock references are left out at the moment and will be added once
the appropriate bindings have been settled upon.
Signed-off-by: Mark Kettenis <[email protected]>
---
arch/arm64/boot/dts/apple/t8103.dtsi | 83 ++++++++++++++++++++++++++++
1 file changed, 83 insertions(+)
diff --git a/arch/arm64/boot/dts/apple/t8103.dtsi b/arch/arm64/boot/dts/apple/t8103.dtsi
index a1e22a2ea2e5..342e01c6098e 100644
--- a/arch/arm64/boot/dts/apple/t8103.dtsi
+++ b/arch/arm64/boot/dts/apple/t8103.dtsi
@@ -9,6 +9,7 @@
#include <dt-bindings/interrupt-controller/apple-aic.h>
#include <dt-bindings/interrupt-controller/irq.h>
+#include <dt-bindings/pinctrl/apple.h>
/ {
compatible = "apple,t8103", "apple,arm-platform";
@@ -131,5 +132,87 @@ aic: interrupt-controller@23b100000 {
interrupt-controller;
reg = <0x2 0x3b100000 0x0 0x8000>;
};
+
+ pinctrl_ap: pinctrl@23c100000 {
+ compatible = "apple,t8103-pinctrl", "apple,pinctrl";
+ reg = <0x2 0x3c100000 0x0 0x100000>;
+
+ gpio-controller;
+ #gpio-cells = <2>;
+ gpio-ranges = <&pinctrl_ap 0 0 212>;
+
+ interrupt-controller;
+ interrupt-parent = <&aic>;
+ interrupts = <AIC_IRQ 190 IRQ_TYPE_LEVEL_HIGH>,
+ <AIC_IRQ 191 IRQ_TYPE_LEVEL_HIGH>,
+ <AIC_IRQ 192 IRQ_TYPE_LEVEL_HIGH>,
+ <AIC_IRQ 193 IRQ_TYPE_LEVEL_HIGH>,
+ <AIC_IRQ 194 IRQ_TYPE_LEVEL_HIGH>,
+ <AIC_IRQ 195 IRQ_TYPE_LEVEL_HIGH>,
+ <AIC_IRQ 196 IRQ_TYPE_LEVEL_HIGH>;
+
+ pcie_pins: pcie-pins {
+ pinmux = <APPLE_PINMUX(150, 1)>,
+ <APPLE_PINMUX(151, 1)>,
+ <APPLE_PINMUX(32, 1)>;
+ };
+ };
+
+ pinctrl_nub: pinctrl@23d1f0000 {
+ compatible = "apple,t8103-pinctrl", "apple,pinctrl";
+ reg = <0x2 0x3d1f0000 0x0 0x4000>;
+
+ gpio-controller;
+ #gpio-cells = <2>;
+ gpio-ranges = <&pinctrl_nub 0 0 23>;
+
+ interrupt-controller;
+ interrupt-parent = <&aic>;
+ interrupts = <AIC_IRQ 330 IRQ_TYPE_LEVEL_HIGH>,
+ <AIC_IRQ 331 IRQ_TYPE_LEVEL_HIGH>,
+ <AIC_IRQ 332 IRQ_TYPE_LEVEL_HIGH>,
+ <AIC_IRQ 333 IRQ_TYPE_LEVEL_HIGH>,
+ <AIC_IRQ 334 IRQ_TYPE_LEVEL_HIGH>,
+ <AIC_IRQ 335 IRQ_TYPE_LEVEL_HIGH>,
+ <AIC_IRQ 336 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ pinctrl_smc: pinctrl@23e820000 {
+ compatible = "apple,t8103-pinctrl", "apple,pinctrl";
+ reg = <0x2 0x3e820000 0x0 0x4000>;
+
+ gpio-controller;
+ #gpio-cells = <2>;
+ gpio-ranges = <&pinctrl_smc 0 0 16>;
+
+ interrupt-controller;
+ interrupt-parent = <&aic>;
+ interrupts = <AIC_IRQ 391 IRQ_TYPE_LEVEL_HIGH>,
+ <AIC_IRQ 392 IRQ_TYPE_LEVEL_HIGH>,
+ <AIC_IRQ 393 IRQ_TYPE_LEVEL_HIGH>,
+ <AIC_IRQ 394 IRQ_TYPE_LEVEL_HIGH>,
+ <AIC_IRQ 395 IRQ_TYPE_LEVEL_HIGH>,
+ <AIC_IRQ 396 IRQ_TYPE_LEVEL_HIGH>,
+ <AIC_IRQ 397 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ pinctrl_aop: pinctrl@24a820000 {
+ compatible = "apple,t8103-pinctrl", "apple,pinctrl";
+ reg = <0x2 0x4a820000 0x0 0x4000>;
+
+ gpio-controller;
+ #gpio-cells = <2>;
+ gpio-ranges = <&pinctrl_aop 0 0 42>;
+
+ interrupt-controller;
+ interrupt-parent = <&aic>;
+ interrupts = <AIC_IRQ 268 IRQ_TYPE_LEVEL_HIGH>,
+ <AIC_IRQ 269 IRQ_TYPE_LEVEL_HIGH>,
+ <AIC_IRQ 270 IRQ_TYPE_LEVEL_HIGH>,
+ <AIC_IRQ 271 IRQ_TYPE_LEVEL_HIGH>,
+ <AIC_IRQ 272 IRQ_TYPE_LEVEL_HIGH>,
+ <AIC_IRQ 273 IRQ_TYPE_LEVEL_HIGH>,
+ <AIC_IRQ 274 IRQ_TYPE_LEVEL_HIGH>;
+ };
};
};
--
2.30.2
From: Mark Kettenis <[email protected]>
Add node corresponding to the apcie,t8103 node in the Apple device tree
for the Mac mini (M1, 2020).
Clock references are left out at the moment and will be added once the
appropriate bindings have been settled on.
Signed-off-by: Mark Kettenis <[email protected]>
Signed-off-by: Alyssa Rosenzweig <[email protected]>
---
arch/arm64/boot/dts/apple/t8103.dtsi | 124 +++++++++++++++++++++++++++
1 file changed, 124 insertions(+)
diff --git a/arch/arm64/boot/dts/apple/t8103.dtsi b/arch/arm64/boot/dts/apple/t8103.dtsi
index 342e01c6098e..c0d3b2fb0366 100644
--- a/arch/arm64/boot/dts/apple/t8103.dtsi
+++ b/arch/arm64/boot/dts/apple/t8103.dtsi
@@ -214,5 +214,129 @@ pinctrl_aop: pinctrl@24a820000 {
<AIC_IRQ 273 IRQ_TYPE_LEVEL_HIGH>,
<AIC_IRQ 274 IRQ_TYPE_LEVEL_HIGH>;
};
+
+ pcie0_dart_0: dart@681008000 {
+ compatible = "apple,t8103-dart";
+ reg = <0x6 0x81008000 0x0 0x4000>;
+ #iommu-cells = <1>;
+ interrupt-parent = <&aic>;
+ interrupts = <AIC_IRQ 696 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ pcie0_dart_1: dart@682008000 {
+ compatible = "apple,t8103-dart";
+ reg = <0x6 0x82008000 0x0 0x4000>;
+ #iommu-cells = <1>;
+ interrupt-parent = <&aic>;
+ interrupts = <AIC_IRQ 699 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ pcie0_dart_2: dart@683008000 {
+ compatible = "apple,t8103-dart";
+ reg = <0x6 0x83008000 0x0 0x4000>;
+ #iommu-cells = <1>;
+ interrupt-parent = <&aic>;
+ interrupts = <AIC_IRQ 702 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ pcie0: pcie@690000000 {
+ compatible = "apple,t8103-pcie", "apple,pcie";
+ device_type = "pci";
+
+ reg = <0x6 0x90000000 0x0 0x1000000>,
+ <0x6 0x80000000 0x0 0x100000>,
+ <0x6 0x81000000 0x0 0x4000>,
+ <0x6 0x82000000 0x0 0x4000>,
+ <0x6 0x83000000 0x0 0x4000>;
+ reg-names = "config", "rc", "port0", "port1", "port2";
+
+ interrupt-parent = <&aic>;
+ interrupts = <AIC_IRQ 695 IRQ_TYPE_LEVEL_HIGH>,
+ <AIC_IRQ 698 IRQ_TYPE_LEVEL_HIGH>,
+ <AIC_IRQ 701 IRQ_TYPE_LEVEL_HIGH>,
+ <AIC_IRQ 704 IRQ_TYPE_LEVEL_HIGH>,
+ <AIC_IRQ 705 IRQ_TYPE_LEVEL_HIGH>,
+ <AIC_IRQ 706 IRQ_TYPE_LEVEL_HIGH>,
+ <AIC_IRQ 707 IRQ_TYPE_LEVEL_HIGH>,
+ <AIC_IRQ 708 IRQ_TYPE_LEVEL_HIGH>,
+ <AIC_IRQ 709 IRQ_TYPE_LEVEL_HIGH>,
+ <AIC_IRQ 710 IRQ_TYPE_LEVEL_HIGH>,
+ <AIC_IRQ 711 IRQ_TYPE_LEVEL_HIGH>,
+ <AIC_IRQ 712 IRQ_TYPE_LEVEL_HIGH>,
+ <AIC_IRQ 713 IRQ_TYPE_LEVEL_HIGH>,
+ <AIC_IRQ 714 IRQ_TYPE_LEVEL_HIGH>,
+ <AIC_IRQ 715 IRQ_TYPE_LEVEL_HIGH>,
+ <AIC_IRQ 716 IRQ_TYPE_LEVEL_HIGH>,
+ <AIC_IRQ 717 IRQ_TYPE_LEVEL_HIGH>,
+ <AIC_IRQ 718 IRQ_TYPE_LEVEL_HIGH>,
+ <AIC_IRQ 719 IRQ_TYPE_LEVEL_HIGH>,
+ <AIC_IRQ 720 IRQ_TYPE_LEVEL_HIGH>,
+ <AIC_IRQ 721 IRQ_TYPE_LEVEL_HIGH>,
+ <AIC_IRQ 722 IRQ_TYPE_LEVEL_HIGH>,
+ <AIC_IRQ 723 IRQ_TYPE_LEVEL_HIGH>,
+ <AIC_IRQ 724 IRQ_TYPE_LEVEL_HIGH>,
+ <AIC_IRQ 725 IRQ_TYPE_LEVEL_HIGH>,
+ <AIC_IRQ 726 IRQ_TYPE_LEVEL_HIGH>,
+ <AIC_IRQ 727 IRQ_TYPE_LEVEL_HIGH>,
+ <AIC_IRQ 728 IRQ_TYPE_LEVEL_HIGH>,
+ <AIC_IRQ 729 IRQ_TYPE_LEVEL_HIGH>,
+ <AIC_IRQ 730 IRQ_TYPE_LEVEL_HIGH>,
+ <AIC_IRQ 731 IRQ_TYPE_LEVEL_HIGH>,
+ <AIC_IRQ 732 IRQ_TYPE_LEVEL_HIGH>,
+ <AIC_IRQ 733 IRQ_TYPE_LEVEL_HIGH>,
+ <AIC_IRQ 734 IRQ_TYPE_LEVEL_HIGH>,
+ <AIC_IRQ 735 IRQ_TYPE_LEVEL_HIGH>;
+
+ msi-controller;
+ msi-parent = <&pcie0>;
+ msi-ranges = <704 32>;
+
+ iommu-map = <0x100 &pcie0_dart_0 0 1>,
+ <0x200 &pcie0_dart_1 0 1>,
+ <0x300 &pcie0_dart_2 0 1>;
+ iommu-map-mask = <0xff00>;
+
+ bus-range = <0 3>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+ ranges = <0x43000000 0x6 0xa0000000 0x6 0xa0000000 0x0 0x20000000>,
+ <0x02000000 0x0 0xc0000000 0x6 0xc0000000 0x0 0x40000000>;
+
+ pinctrl-0 = <&pcie_pins>;
+ pinctrl-names = "default";
+
+ pci@0,0 {
+ device_type = "pci";
+ reg = <0x0 0x0 0x0 0x0 0x0>;
+ reset-gpios = <&pinctrl_ap 152 0>;
+ max-link-speed = <2>;
+
+ #address-cells = <3>;
+ #size-cells = <2>;
+ ranges;
+ };
+
+ pci@1,0 {
+ device_type = "pci";
+ reg = <0x800 0x0 0x0 0x0 0x0>;
+ reset-gpios = <&pinctrl_ap 153 0>;
+ max-link-speed = <2>;
+
+ #address-cells = <3>;
+ #size-cells = <2>;
+ ranges;
+ };
+
+ pci@2,0 {
+ device_type = "pci";
+ reg = <0x1000 0x0 0x0 0x0 0x0>;
+ reset-gpios = <&pinctrl_ap 33 0>;
+ max-link-speed = <1>;
+
+ #address-cells = <3>;
+ #size-cells = <2>;
+ ranges;
+ };
+ };
};
};
--
2.30.2
> From: Alyssa Rosenzweig <[email protected]>
> Date: Sun, 15 Aug 2021 23:16:21 -0400
>
> From: Mark Kettenis <[email protected]>
>
> Add node corresponding to the apcie,t8103 node in the Apple device tree
> for the Mac mini (M1, 2020).
>
> Clock references are left out at the moment and will be added once the
> appropriate bindings have been settled on.
>
> Signed-off-by: Mark Kettenis <[email protected]>
> Signed-off-by: Alyssa Rosenzweig <[email protected]>
> ---
> arch/arm64/boot/dts/apple/t8103.dtsi | 124 +++++++++++++++++++++++++++
> 1 file changed, 124 insertions(+)
>
> diff --git a/arch/arm64/boot/dts/apple/t8103.dtsi b/arch/arm64/boot/dts/apple/t8103.dtsi
> index 342e01c6098e..c0d3b2fb0366 100644
> --- a/arch/arm64/boot/dts/apple/t8103.dtsi
> +++ b/arch/arm64/boot/dts/apple/t8103.dtsi
> @@ -214,5 +214,129 @@ pinctrl_aop: pinctrl@24a820000 {
> <AIC_IRQ 273 IRQ_TYPE_LEVEL_HIGH>,
> <AIC_IRQ 274 IRQ_TYPE_LEVEL_HIGH>;
> };
> +
> + pcie0_dart_0: dart@681008000 {
> + compatible = "apple,t8103-dart";
> + reg = <0x6 0x81008000 0x0 0x4000>;
> + #iommu-cells = <1>;
> + interrupt-parent = <&aic>;
> + interrupts = <AIC_IRQ 696 IRQ_TYPE_LEVEL_HIGH>;
> + };
> +
> + pcie0_dart_1: dart@682008000 {
> + compatible = "apple,t8103-dart";
> + reg = <0x6 0x82008000 0x0 0x4000>;
> + #iommu-cells = <1>;
> + interrupt-parent = <&aic>;
> + interrupts = <AIC_IRQ 699 IRQ_TYPE_LEVEL_HIGH>;
> + };
> +
> + pcie0_dart_2: dart@683008000 {
> + compatible = "apple,t8103-dart";
> + reg = <0x6 0x83008000 0x0 0x4000>;
> + #iommu-cells = <1>;
> + interrupt-parent = <&aic>;
> + interrupts = <AIC_IRQ 702 IRQ_TYPE_LEVEL_HIGH>;
> + };
> +
> + pcie0: pcie@690000000 {
> + compatible = "apple,t8103-pcie", "apple,pcie";
> + device_type = "pci";
> +
> + reg = <0x6 0x90000000 0x0 0x1000000>,
> + <0x6 0x80000000 0x0 0x100000>,
> + <0x6 0x81000000 0x0 0x4000>,
> + <0x6 0x82000000 0x0 0x4000>,
> + <0x6 0x83000000 0x0 0x4000>;
> + reg-names = "config", "rc", "port0", "port1", "port2";
> +
> + interrupt-parent = <&aic>;
> + interrupts = <AIC_IRQ 695 IRQ_TYPE_LEVEL_HIGH>,
> + <AIC_IRQ 698 IRQ_TYPE_LEVEL_HIGH>,
> + <AIC_IRQ 701 IRQ_TYPE_LEVEL_HIGH>,
> + <AIC_IRQ 704 IRQ_TYPE_LEVEL_HIGH>,
> + <AIC_IRQ 705 IRQ_TYPE_LEVEL_HIGH>,
> + <AIC_IRQ 706 IRQ_TYPE_LEVEL_HIGH>,
> + <AIC_IRQ 707 IRQ_TYPE_LEVEL_HIGH>,
> + <AIC_IRQ 708 IRQ_TYPE_LEVEL_HIGH>,
> + <AIC_IRQ 709 IRQ_TYPE_LEVEL_HIGH>,
> + <AIC_IRQ 710 IRQ_TYPE_LEVEL_HIGH>,
> + <AIC_IRQ 711 IRQ_TYPE_LEVEL_HIGH>,
> + <AIC_IRQ 712 IRQ_TYPE_LEVEL_HIGH>,
> + <AIC_IRQ 713 IRQ_TYPE_LEVEL_HIGH>,
> + <AIC_IRQ 714 IRQ_TYPE_LEVEL_HIGH>,
> + <AIC_IRQ 715 IRQ_TYPE_LEVEL_HIGH>,
> + <AIC_IRQ 716 IRQ_TYPE_LEVEL_HIGH>,
> + <AIC_IRQ 717 IRQ_TYPE_LEVEL_HIGH>,
> + <AIC_IRQ 718 IRQ_TYPE_LEVEL_HIGH>,
> + <AIC_IRQ 719 IRQ_TYPE_LEVEL_HIGH>,
> + <AIC_IRQ 720 IRQ_TYPE_LEVEL_HIGH>,
> + <AIC_IRQ 721 IRQ_TYPE_LEVEL_HIGH>,
> + <AIC_IRQ 722 IRQ_TYPE_LEVEL_HIGH>,
> + <AIC_IRQ 723 IRQ_TYPE_LEVEL_HIGH>,
> + <AIC_IRQ 724 IRQ_TYPE_LEVEL_HIGH>,
> + <AIC_IRQ 725 IRQ_TYPE_LEVEL_HIGH>,
> + <AIC_IRQ 726 IRQ_TYPE_LEVEL_HIGH>,
> + <AIC_IRQ 727 IRQ_TYPE_LEVEL_HIGH>,
> + <AIC_IRQ 728 IRQ_TYPE_LEVEL_HIGH>,
> + <AIC_IRQ 729 IRQ_TYPE_LEVEL_HIGH>,
> + <AIC_IRQ 730 IRQ_TYPE_LEVEL_HIGH>,
> + <AIC_IRQ 731 IRQ_TYPE_LEVEL_HIGH>,
> + <AIC_IRQ 732 IRQ_TYPE_LEVEL_HIGH>,
> + <AIC_IRQ 733 IRQ_TYPE_LEVEL_HIGH>,
> + <AIC_IRQ 734 IRQ_TYPE_LEVEL_HIGH>,
> + <AIC_IRQ 735 IRQ_TYPE_LEVEL_HIGH>;
> +
> + msi-controller;
> + msi-parent = <&pcie0>;
> + msi-ranges = <704 32>;
> +
> + iommu-map = <0x100 &pcie0_dart_0 0 1>,
> + <0x200 &pcie0_dart_1 0 1>,
> + <0x300 &pcie0_dart_2 0 1>;
> + iommu-map-mask = <0xff00>;
So this will need a little bit more thought.
The PCIe bridge hardware has logic to map a PCIe Requester ID (RID) to
an IOMMU Stream ID (SID). The RID is basically just the PCI
bus/device/function number of the PCI device that initiates the DMA.
As far as I can tell if the RID isn't matched by the PCIe bridge
RID-to-SID mapping hardware it will be mapped to SID 0. Your driver
doesn't program the RID-to-SID hardware so using 0 as the SID in your
device tree makes some sense.
However, since SID 0 is the default used when there is no match for an
RID, we should probably avoid it if we can. That's why in my
apple,pcie DT binding series I used SID 1. But this would require
additional code in the driver to parse the iommu-map property and
program the RID-to-SID hardware accordingly.
Now until we support the Tunderbolt ports, this isn't all that
important and we can go with your current driver and DT. I can adjust
the U-Boot DT accordingly.
> +
> + bus-range = <0 3>;
> + #address-cells = <3>;
> + #size-cells = <2>;
> + ranges = <0x43000000 0x6 0xa0000000 0x6 0xa0000000 0x0 0x20000000>,
> + <0x02000000 0x0 0xc0000000 0x6 0xc0000000 0x0 0x40000000>;
> +
> + pinctrl-0 = <&pcie_pins>;
> + pinctrl-names = "default";
> +
> + pci@0,0 {
> + device_type = "pci";
> + reg = <0x0 0x0 0x0 0x0 0x0>;
> + reset-gpios = <&pinctrl_ap 152 0>;
> + max-link-speed = <2>;
> +
> + #address-cells = <3>;
> + #size-cells = <2>;
> + ranges;
> + };
> +
> + pci@1,0 {
> + device_type = "pci";
> + reg = <0x800 0x0 0x0 0x0 0x0>;
> + reset-gpios = <&pinctrl_ap 153 0>;
> + max-link-speed = <2>;
> +
> + #address-cells = <3>;
> + #size-cells = <2>;
> + ranges;
> + };
> +
> + pci@2,0 {
> + device_type = "pci";
> + reg = <0x1000 0x0 0x0 0x0 0x0>;
> + reset-gpios = <&pinctrl_ap 33 0>;
> + max-link-speed = <1>;
> +
> + #address-cells = <3>;
> + #size-cells = <2>;
> + ranges;
> + };
> + };
> };
> };
> --
> 2.30.2
>
>
> From: Alyssa Rosenzweig <[email protected]>
> Date: Sun, 15 Aug 2021 23:16:17 -0400
>
> Add a minimal driver to bring up the PCIe bus on Apple system-on-chips,
> particularly the Apple M1. This driver exposes the internal bus used for
> the USB type-A ports, Ethernet, Wi-Fi, and Bluetooth. Bringing up the
> radios requires additional drivers beyond what's necessary for PCIe
> itself.
>
> In this patch, a minimal driver is added that brings up the PCIe bus on
> probe. This logic is derived from Corellium's driver via Mark Kettenis's
> U-Boot patches.
>
> Co-developed-by: Stan Skowronek <[email protected]>
> Signed-off-by: Stan Skowronek <[email protected]>
> Signed-off-by: Alyssa Rosenzweig <[email protected]>
> ---
> MAINTAINERS | 6 +
> drivers/pci/controller/Kconfig | 12 ++
> drivers/pci/controller/Makefile | 1 +
> drivers/pci/controller/pcie-apple.c | 251 ++++++++++++++++++++++++++++
> 4 files changed, 270 insertions(+)
> create mode 100644 drivers/pci/controller/pcie-apple.c
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index a5687cf6f925..47cb3d320c56 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -1269,6 +1269,12 @@ S: Maintained
> F: Documentation/devicetree/bindings/iommu/apple,dart.yaml
> F: drivers/iommu/apple-dart.c
>
> +APPLE PCIE CONTROLLER DRIVER
> +M: Alyssa Rosenzweig <[email protected]>
> +L: [email protected]
> +S: Maintained
> +F: drivers/pci/controller/pcie-apple.c
> +
> APPLE SMC DRIVER
> M: Henrik Rydberg <[email protected]>
> L: [email protected]
> diff --git a/drivers/pci/controller/Kconfig b/drivers/pci/controller/Kconfig
> index 326f7d13024f..814833a8120d 100644
> --- a/drivers/pci/controller/Kconfig
> +++ b/drivers/pci/controller/Kconfig
> @@ -312,6 +312,18 @@ 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
> + tristate "Apple PCIe controller"
> + depends on ARCH_APPLE || COMPILE_TEST
> + depends on OF
> + depends on PCI_MSI_IRQ_DOMAIN
> + 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"
> diff --git a/drivers/pci/controller/Makefile b/drivers/pci/controller/Makefile
> index aaf30b3dcc14..f9d40bad932c 100644
> --- a/drivers/pci/controller/Makefile
> +++ b/drivers/pci/controller/Makefile
> @@ -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/
> diff --git a/drivers/pci/controller/pcie-apple.c b/drivers/pci/controller/pcie-apple.c
> new file mode 100644
> index 000000000000..a1efcc3373ea
> --- /dev/null
> +++ b/drivers/pci/controller/pcie-apple.c
> @@ -0,0 +1,251 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * PCIe host bridge driver for Apple system-on-chips.
> + *
> + * The HW is ECAM compliant, so once the controller is initialized, the driver
> + * mostly only needs MSI handling. Initialization requires enabling power and
> + * clocks, along with a number of register pokes.
> + *
> + * Copyright (C) 2021 Alyssa Rosenzweig <[email protected]>
> + * Copyright (C) 2021 Google LLC
> + * Copyright (C) 2021 Corellium LLC
> + * Copyright (C) 2021 Mark Kettenis <[email protected]>
> + *
> + * Author: Alyssa Rosenzweig <[email protected]>
> + * Author: Marc Zyngier <[email protected]>
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/irqdomain.h>
> +#include <linux/module.h>
> +#include <linux/msi.h>
> +#include <linux/of_irq.h>
> +#include <linux/pci-ecam.h>
> +#include <linux/iopoll.h>
> +#include <linux/gpio/consumer.h>
> +
> +#define CORE_RC_PHYIF_CTL 0x00024
> +#define CORE_RC_PHYIF_CTL_RUN BIT(0)
> +#define CORE_RC_PHYIF_STAT 0x00028
> +#define CORE_RC_PHYIF_STAT_REFCLK BIT(4)
> +#define CORE_RC_CTL 0x00050
> +#define CORE_RC_CTL_RUN BIT(0)
> +#define CORE_RC_STAT 0x00058
> +#define CORE_RC_STAT_READY BIT(0)
> +#define CORE_FABRIC_STAT 0x04000
> +#define CORE_FABRIC_STAT_MASK 0x001F001F
> +#define CORE_LANE_CFG(port) (0x84000 + 0x4000 * (port))
> +#define CORE_LANE_CFG_REFCLK0REQ BIT(0)
> +#define CORE_LANE_CFG_REFCLK1 BIT(1)
> +#define CORE_LANE_CFG_REFCLK0ACK BIT(2)
> +#define CORE_LANE_CFG_REFCLKEN (BIT(9) | BIT(10))
> +#define CORE_LANE_CTL(port) (0x84004 + 0x4000 * (port))
> +#define CORE_LANE_CTL_CFGACC BIT(15)
> +
> +#define PORT_LTSSMCTL 0x00080
> +#define PORT_LTSSMCTL_START BIT(0)
> +#define PORT_INTSTAT 0x00100
> +#define PORT_INT_TUNNEL_ERR BIT(31)
> +#define PORT_INT_CPL_TIMEOUT BIT(23)
> +#define PORT_INT_RID2SID_MAPERR BIT(22)
> +#define PORT_INT_CPL_ABORT BIT(21)
> +#define PORT_INT_MSI_BAD_DATA BIT(19)
> +#define PORT_INT_MSI_ERR BIT(18)
> +#define PORT_INT_REQADDR_GT32 BIT(17)
> +#define PORT_INT_AF_TIMEOUT BIT(15)
> +#define PORT_INT_LINK_DOWN BIT(14)
> +#define PORT_INT_LINK_UP BIT(12)
> +#define PORT_INT_LINK_BWMGMT BIT(11)
> +#define PORT_INT_AER_MASK (15 << 4)
> +#define PORT_INT_PORT_ERR BIT(4)
> +#define PORT_INT_INTx(i) BIT(i)
> +#define PORT_INT_INTxALL 15
> +#define PORT_INTMSK 0x00104
> +#define PORT_INTMSKSET 0x00108
> +#define PORT_INTMSKCLR 0x0010c
> +#define PORT_MSICFG 0x00124
> +#define PORT_MSICFG_EN BIT(0)
> +#define PORT_MSICFG_L2MSINUM_SHIFT 4
> +#define PORT_MSIBASE 0x00128
> +#define PORT_MSIBASE_1_SHIFT 16
> +#define PORT_MSIADDR 0x00168
> +#define PORT_LINKSTS 0x00208
> +#define PORT_LINKSTS_UP BIT(0)
> +#define PORT_LINKSTS_BUSY BIT(2)
> +#define PORT_LINKCMDSTS 0x00210
> +#define PORT_OUTS_NPREQS 0x00284
> +#define PORT_OUTS_NPREQS_REQ BIT(24)
> +#define PORT_OUTS_NPREQS_CPL BIT(16)
> +#define PORT_RXWR_FIFO 0x00288
> +#define PORT_RXWR_FIFO_HDR GENMASK(15, 10)
> +#define PORT_RXWR_FIFO_DATA GENMASK(9, 0)
> +#define PORT_RXRD_FIFO 0x0028C
> +#define PORT_RXRD_FIFO_REQ GENMASK(6, 0)
> +#define PORT_OUTS_CPLS 0x00290
> +#define PORT_OUTS_CPLS_SHRD GENMASK(14, 8)
> +#define PORT_OUTS_CPLS_WAIT GENMASK(6, 0)
> +#define PORT_APPCLK 0x00800
> +#define PORT_APPCLK_EN BIT(0)
> +#define PORT_APPCLK_CGDIS BIT(8)
> +#define PORT_STATUS 0x00804
> +#define PORT_STATUS_READY BIT(0)
> +#define PORT_REFCLK 0x00810
> +#define PORT_REFCLK_EN BIT(0)
> +#define PORT_REFCLK_CGDIS BIT(8)
> +#define PORT_PERST 0x00814
> +#define PORT_PERST_OFF BIT(0)
> +#define PORT_RID2SID(i16) (0x00828 + 4 * (i16))
> +#define PORT_RID2SID_VALID BIT(31)
> +#define PORT_RID2SID_SID_SHIFT 16
> +#define PORT_RID2SID_BUS_SHIFT 8
> +#define PORT_RID2SID_DEV_SHIFT 3
> +#define PORT_RID2SID_FUNC_SHIFT 0
> +#define PORT_OUTS_PREQS_HDR 0x00980
> +#define PORT_OUTS_PREQS_HDR_MASK GENMASK(9, 0)
> +#define PORT_OUTS_PREQS_DATA 0x00984
> +#define PORT_OUTS_PREQS_DATA_MASK GENMASK(15, 0)
> +#define PORT_TUNCTRL 0x00988
> +#define PORT_TUNCTRL_PERST_ON BIT(0)
> +#define PORT_TUNCTRL_PERST_ACK_REQ BIT(1)
> +#define PORT_TUNSTAT 0x0098c
> +#define PORT_TUNSTAT_PERST_ON BIT(0)
> +#define PORT_TUNSTAT_PERST_ACK_PEND BIT(1)
> +#define PORT_PREFMEM_ENABLE 0x00994
> +
> +/* The doorbell address is "well known" */
> +#define DOORBELL_ADDR 0xfffff000
> +
> +struct apple_pcie {
> + struct mutex lock;
> + struct device *dev;
> + void __iomem *rc;
> +};
> +
> +static inline void rmwl(u32 clr, u32 set, void __iomem *addr)
> +{
> + writel_relaxed((readl_relaxed(addr) & ~clr) | set, addr);
> +}
> +
> +static int apple_pcie_setup_port(struct apple_pcie *pcie,
> + struct gpio_desc *reset,
> + unsigned int i)
> +{
> + struct platform_device *platform = to_platform_device(pcie->dev);
> + void __iomem *port;
> + uint32_t stat;
> + int ret;
> +
> + port = devm_platform_ioremap_resource(platform, i + 2);
> +
> + if (IS_ERR(port))
> + return -ENODEV;
> +
> + /* Skip setup if the link was already enabled by the bootloader */
> + if (readl_relaxed(port + PORT_LINKSTS) & PORT_LINKSTS_UP)
> + return 0;
> +
> + rmwl(0, PORT_PERST_OFF, port + PORT_PERST);
> + gpiod_set_value(reset, 1);
> +
> + ret = readl_relaxed_poll_timeout(port + PORT_STATUS, stat,
> + stat & PORT_STATUS_READY, 100, 250000);
> + if (ret < 0) {
> + dev_err(pcie->dev, "port %u ready wait timeout\n", i);
> + return ret;
> + }
> +
> + /* Configure MSIs */
> + writel_relaxed(DOORBELL_ADDR, port + PORT_MSIADDR);
> + writel_relaxed(0, port + PORT_MSIBASE);
> +
> + /* Enable 32 MSIs */
> + writel_relaxed((5 << PORT_MSICFG_L2MSINUM_SHIFT) | PORT_MSICFG_EN,
> + port + PORT_MSICFG);
The MSI configuration writes should probably be part of patch 4/6.
> +
> + /* Enable link interrupts */
> + writel_relaxed(0xfb512fff, port + PORT_INTMSKSET);
So this magic number should probably be the bitwise inverse of the
value used below:
> +
> + writel_relaxed(PORT_INT_LINK_UP | PORT_INT_LINK_DOWN |
> + PORT_INT_AF_TIMEOUT | PORT_INT_REQADDR_GT32 |
> + PORT_INT_MSI_ERR | PORT_INT_MSI_BAD_DATA |
> + PORT_INT_CPL_ABORT | PORT_INT_CPL_TIMEOUT | (1 << 26),
> + port + PORT_INTSTAT);
My suggestion would be to introduce a #define PORT_INT_DEFAULT for
that collection of bits and use ~PORT_INT_DEFAULT in the first write
and PORT_INT_DEFAULT in the latter.
Wonder if we can figure out what bit 26 means. Or maybe we don't
really need to enable that interrupt and can forget about it? The
Corellium code doesn't handle that bit in its interrupt handler, so
unless there is some magic going on we should be able to simply ignore
it.
> +
> + /* Flush writes and enable the link */
> + dma_wmb();
> + writel_relaxed(PORT_LTSSMCTL_START, port + PORT_LTSSMCTL);
> +
> + return 0;
> +}
> +
> +static int apple_m1_pci_init(struct pci_config_window *cfg)
> +{
> + struct device *dev = cfg->parent;
> + struct platform_device *platform = to_platform_device(dev);
> +
> + struct apple_pcie *pcie;
> + struct device_node *of_port;
> + int ret, i;
> +
> + pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL);
> + if (!pcie)
> + return -ENOMEM;
> +
> + pcie->dev = dev;
> +
> + mutex_init(&pcie->lock);
> +
> + pcie->rc = devm_platform_ioremap_resource(platform, 1);
> +
> + if (IS_ERR(pcie->rc))
> + return -ENODEV;
> +
> + i = 0;
> +
> + for_each_child_of_node(dev->of_node, of_port) {
> + struct gpio_desc *reset;
> +
> + reset = gpiod_get_from_of_node(of_port, "reset-gpios", 0,
> + GPIOD_OUT_LOW, "#PERST");
> + if (IS_ERR(reset))
> + return PTR_ERR(reset);
> +
> + ret = apple_pcie_setup_port(pcie, reset, i);
> +
> + if (ret) {
> + dev_err(pcie->dev, "Port %u setup fail: %d\n", i, ret);
> + return ret;
> + }
> +
> + ++i;
> + }
> +
> + return 0;
> +}
> +
> +static const struct pci_ecam_ops apple_m1_cfg_ecam_ops = {
> + .init = apple_m1_pci_init,
> + .pci_ops = {
> + .map_bus = pci_ecam_map_bus,
> + .read = pci_generic_config_read,
> + .write = pci_generic_config_write,
> + }
> +};
> +
> +static const struct of_device_id apple_pci_of_match[] = {
> + { .compatible = "apple,pcie", .data = &apple_m1_cfg_ecam_ops },
> + { }
> +};
> +MODULE_DEVICE_TABLE(of, apple_pci_of_match);
> +
> +static struct platform_driver apple_pci_driver = {
> + .driver = {
> + .name = "pcie-apple",
> + .of_match_table = apple_pci_of_match,
> + },
> + .probe = pci_host_common_probe,
> + .remove = pci_host_common_remove,
> +};
> +module_platform_driver(apple_pci_driver);
> +
> +MODULE_LICENSE("GPL v2");
> --
> 2.30.2
>
>