Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

PCI: apple: Add initial hardware bring-up

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.

Co-developed-by: Stan Skowronek <stan@corellium.com>
Link: https://lore.kernel.org/r/20210929163847.2807812-5-maz@kernel.org
Signed-off-by: Stan Skowronek <stan@corellium.com>
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
Signed-off-by: Marc Zyngier <maz@kernel.org>
Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Reviewed-by: Rob Herring <robh@kernel.org>
Reviewed-by: Sven Peter <sven@svenpeter.dev>

authored by

Alyssa Rosenzweig and committed by
Bjorn Helgaas
1e33888f 978fd005

+259
+7
MAINTAINERS
··· 1280 1280 F: Documentation/devicetree/bindings/iommu/apple,dart.yaml 1281 1281 F: drivers/iommu/apple-dart.c 1282 1282 1283 + APPLE PCIE CONTROLLER DRIVER 1284 + M: Alyssa Rosenzweig <alyssa@rosenzweig.io> 1285 + M: Marc Zyngier <maz@kernel.org> 1286 + L: linux-pci@vger.kernel.org 1287 + S: Maintained 1288 + F: drivers/pci/controller/pcie-apple.c 1289 + 1283 1290 APPLE SMC DRIVER 1284 1291 M: Henrik Rydberg <rydberg@bitmath.org> 1285 1292 L: linux-hwmon@vger.kernel.org
+13
drivers/pci/controller/Kconfig
··· 312 312 Say Y here if you want error handling support 313 313 for the PCIe controller's errors on HiSilicon HIP SoCs 314 314 315 + config PCIE_APPLE 316 + tristate "Apple PCIe controller" 317 + depends on ARCH_APPLE || COMPILE_TEST 318 + depends on OF 319 + depends on PCI_MSI_IRQ_DOMAIN 320 + select PCI_HOST_COMMON 321 + help 322 + Say Y here if you want to enable PCIe controller support on Apple 323 + system-on-chips, like the Apple M1. This is required for the USB 324 + type-A ports, Ethernet, Wi-Fi, and Bluetooth. 325 + 326 + If unsure, say Y if you have an Apple Silicon system. 327 + 315 328 source "drivers/pci/controller/dwc/Kconfig" 316 329 source "drivers/pci/controller/mobiveil/Kconfig" 317 330 source "drivers/pci/controller/cadence/Kconfig"
+1
drivers/pci/controller/Makefile
··· 37 37 obj-$(CONFIG_PCIE_BRCMSTB) += pcie-brcmstb.o 38 38 obj-$(CONFIG_PCI_LOONGSON) += pci-loongson.o 39 39 obj-$(CONFIG_PCIE_HISI_ERR) += pcie-hisi-error.o 40 + obj-$(CONFIG_PCIE_APPLE) += pcie-apple.o 40 41 # pcie-hisi.o quirks are needed even without CONFIG_PCIE_DW 41 42 obj-y += dwc/ 42 43 obj-y += mobiveil/
+238
drivers/pci/controller/pcie-apple.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * PCIe host bridge driver for Apple system-on-chips. 4 + * 5 + * The HW is ECAM compliant, so once the controller is initialized, 6 + * the driver mostly deals MSI mapping and handling of per-port 7 + * interrupts (INTx, management and error signals). 8 + * 9 + * Initialization requires enabling power and clocks, along with a 10 + * number of register pokes. 11 + * 12 + * Copyright (C) 2021 Alyssa Rosenzweig <alyssa@rosenzweig.io> 13 + * Copyright (C) 2021 Google LLC 14 + * Copyright (C) 2021 Corellium LLC 15 + * Copyright (C) 2021 Mark Kettenis <kettenis@openbsd.org> 16 + * 17 + * Author: Alyssa Rosenzweig <alyssa@rosenzweig.io> 18 + * Author: Marc Zyngier <maz@kernel.org> 19 + */ 20 + 21 + #include <linux/gpio/consumer.h> 22 + #include <linux/kernel.h> 23 + #include <linux/iopoll.h> 24 + #include <linux/irqdomain.h> 25 + #include <linux/module.h> 26 + #include <linux/msi.h> 27 + #include <linux/of_irq.h> 28 + #include <linux/pci-ecam.h> 29 + 30 + #define CORE_RC_PHYIF_CTL 0x00024 31 + #define CORE_RC_PHYIF_CTL_RUN BIT(0) 32 + #define CORE_RC_PHYIF_STAT 0x00028 33 + #define CORE_RC_PHYIF_STAT_REFCLK BIT(4) 34 + #define CORE_RC_CTL 0x00050 35 + #define CORE_RC_CTL_RUN BIT(0) 36 + #define CORE_RC_STAT 0x00058 37 + #define CORE_RC_STAT_READY BIT(0) 38 + #define CORE_FABRIC_STAT 0x04000 39 + #define CORE_FABRIC_STAT_MASK 0x001F001F 40 + #define CORE_LANE_CFG(port) (0x84000 + 0x4000 * (port)) 41 + #define CORE_LANE_CFG_REFCLK0REQ BIT(0) 42 + #define CORE_LANE_CFG_REFCLK1 BIT(1) 43 + #define CORE_LANE_CFG_REFCLK0ACK BIT(2) 44 + #define CORE_LANE_CFG_REFCLKEN (BIT(9) | BIT(10)) 45 + #define CORE_LANE_CTL(port) (0x84004 + 0x4000 * (port)) 46 + #define CORE_LANE_CTL_CFGACC BIT(15) 47 + 48 + #define PORT_LTSSMCTL 0x00080 49 + #define PORT_LTSSMCTL_START BIT(0) 50 + #define PORT_INTSTAT 0x00100 51 + #define PORT_INT_TUNNEL_ERR 31 52 + #define PORT_INT_CPL_TIMEOUT 23 53 + #define PORT_INT_RID2SID_MAPERR 22 54 + #define PORT_INT_CPL_ABORT 21 55 + #define PORT_INT_MSI_BAD_DATA 19 56 + #define PORT_INT_MSI_ERR 18 57 + #define PORT_INT_REQADDR_GT32 17 58 + #define PORT_INT_AF_TIMEOUT 15 59 + #define PORT_INT_LINK_DOWN 14 60 + #define PORT_INT_LINK_UP 12 61 + #define PORT_INT_LINK_BWMGMT 11 62 + #define PORT_INT_AER_MASK (15 << 4) 63 + #define PORT_INT_PORT_ERR 4 64 + #define PORT_INT_INTx(i) i 65 + #define PORT_INT_INTx_MASK 15 66 + #define PORT_INTMSK 0x00104 67 + #define PORT_INTMSKSET 0x00108 68 + #define PORT_INTMSKCLR 0x0010c 69 + #define PORT_MSICFG 0x00124 70 + #define PORT_MSICFG_EN BIT(0) 71 + #define PORT_MSICFG_L2MSINUM_SHIFT 4 72 + #define PORT_MSIBASE 0x00128 73 + #define PORT_MSIBASE_1_SHIFT 16 74 + #define PORT_MSIADDR 0x00168 75 + #define PORT_LINKSTS 0x00208 76 + #define PORT_LINKSTS_UP BIT(0) 77 + #define PORT_LINKSTS_BUSY BIT(2) 78 + #define PORT_LINKCMDSTS 0x00210 79 + #define PORT_OUTS_NPREQS 0x00284 80 + #define PORT_OUTS_NPREQS_REQ BIT(24) 81 + #define PORT_OUTS_NPREQS_CPL BIT(16) 82 + #define PORT_RXWR_FIFO 0x00288 83 + #define PORT_RXWR_FIFO_HDR GENMASK(15, 10) 84 + #define PORT_RXWR_FIFO_DATA GENMASK(9, 0) 85 + #define PORT_RXRD_FIFO 0x0028C 86 + #define PORT_RXRD_FIFO_REQ GENMASK(6, 0) 87 + #define PORT_OUTS_CPLS 0x00290 88 + #define PORT_OUTS_CPLS_SHRD GENMASK(14, 8) 89 + #define PORT_OUTS_CPLS_WAIT GENMASK(6, 0) 90 + #define PORT_APPCLK 0x00800 91 + #define PORT_APPCLK_EN BIT(0) 92 + #define PORT_APPCLK_CGDIS BIT(8) 93 + #define PORT_STATUS 0x00804 94 + #define PORT_STATUS_READY BIT(0) 95 + #define PORT_REFCLK 0x00810 96 + #define PORT_REFCLK_EN BIT(0) 97 + #define PORT_REFCLK_CGDIS BIT(8) 98 + #define PORT_PERST 0x00814 99 + #define PORT_PERST_OFF BIT(0) 100 + #define PORT_RID2SID(i16) (0x00828 + 4 * (i16)) 101 + #define PORT_RID2SID_VALID BIT(31) 102 + #define PORT_RID2SID_SID_SHIFT 16 103 + #define PORT_RID2SID_BUS_SHIFT 8 104 + #define PORT_RID2SID_DEV_SHIFT 3 105 + #define PORT_RID2SID_FUNC_SHIFT 0 106 + #define PORT_OUTS_PREQS_HDR 0x00980 107 + #define PORT_OUTS_PREQS_HDR_MASK GENMASK(9, 0) 108 + #define PORT_OUTS_PREQS_DATA 0x00984 109 + #define PORT_OUTS_PREQS_DATA_MASK GENMASK(15, 0) 110 + #define PORT_TUNCTRL 0x00988 111 + #define PORT_TUNCTRL_PERST_ON BIT(0) 112 + #define PORT_TUNCTRL_PERST_ACK_REQ BIT(1) 113 + #define PORT_TUNSTAT 0x0098c 114 + #define PORT_TUNSTAT_PERST_ON BIT(0) 115 + #define PORT_TUNSTAT_PERST_ACK_PEND BIT(1) 116 + #define PORT_PREFMEM_ENABLE 0x00994 117 + 118 + struct apple_pcie { 119 + struct device *dev; 120 + void __iomem *base; 121 + }; 122 + 123 + struct apple_pcie_port { 124 + struct apple_pcie *pcie; 125 + struct device_node *np; 126 + void __iomem *base; 127 + int idx; 128 + }; 129 + 130 + static void rmw_set(u32 set, void __iomem *addr) 131 + { 132 + writel_relaxed(readl_relaxed(addr) | set, addr); 133 + } 134 + 135 + static int apple_pcie_setup_port(struct apple_pcie *pcie, 136 + struct device_node *np) 137 + { 138 + struct platform_device *platform = to_platform_device(pcie->dev); 139 + struct apple_pcie_port *port; 140 + struct gpio_desc *reset; 141 + u32 stat, idx; 142 + int ret; 143 + 144 + reset = gpiod_get_from_of_node(np, "reset-gpios", 0, 145 + GPIOD_OUT_LOW, "#PERST"); 146 + if (IS_ERR(reset)) 147 + return PTR_ERR(reset); 148 + 149 + port = devm_kzalloc(pcie->dev, sizeof(*port), GFP_KERNEL); 150 + if (!port) 151 + return -ENOMEM; 152 + 153 + ret = of_property_read_u32_index(np, "reg", 0, &idx); 154 + if (ret) 155 + return ret; 156 + 157 + /* Use the first reg entry to work out the port index */ 158 + port->idx = idx >> 11; 159 + port->pcie = pcie; 160 + port->np = np; 161 + 162 + port->base = devm_platform_ioremap_resource(platform, port->idx + 2); 163 + if (IS_ERR(port->base)) 164 + return PTR_ERR(port->base); 165 + 166 + rmw_set(PORT_APPCLK_EN, port->base + PORT_APPCLK); 167 + 168 + rmw_set(PORT_PERST_OFF, port->base + PORT_PERST); 169 + gpiod_set_value(reset, 1); 170 + 171 + ret = readl_relaxed_poll_timeout(port->base + PORT_STATUS, stat, 172 + stat & PORT_STATUS_READY, 100, 250000); 173 + if (ret < 0) { 174 + dev_err(pcie->dev, "port %pOF ready wait timeout\n", np); 175 + return ret; 176 + } 177 + 178 + writel_relaxed(PORT_LTSSMCTL_START, port->base + PORT_LTSSMCTL); 179 + 180 + return 0; 181 + } 182 + 183 + static int apple_pcie_init(struct pci_config_window *cfg) 184 + { 185 + struct device *dev = cfg->parent; 186 + struct platform_device *platform = to_platform_device(dev); 187 + struct device_node *of_port; 188 + struct apple_pcie *pcie; 189 + int ret; 190 + 191 + pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL); 192 + if (!pcie) 193 + return -ENOMEM; 194 + 195 + pcie->dev = dev; 196 + 197 + pcie->base = devm_platform_ioremap_resource(platform, 1); 198 + if (IS_ERR(pcie->base)) 199 + return PTR_ERR(pcie->base); 200 + 201 + for_each_child_of_node(dev->of_node, of_port) { 202 + ret = apple_pcie_setup_port(pcie, of_port); 203 + if (ret) { 204 + dev_err(pcie->dev, "Port %pOF setup fail: %d\n", of_port, ret); 205 + of_node_put(of_port); 206 + return ret; 207 + } 208 + } 209 + 210 + return 0; 211 + } 212 + 213 + static const struct pci_ecam_ops apple_pcie_cfg_ecam_ops = { 214 + .init = apple_pcie_init, 215 + .pci_ops = { 216 + .map_bus = pci_ecam_map_bus, 217 + .read = pci_generic_config_read, 218 + .write = pci_generic_config_write, 219 + } 220 + }; 221 + 222 + static const struct of_device_id apple_pcie_of_match[] = { 223 + { .compatible = "apple,pcie", .data = &apple_pcie_cfg_ecam_ops }, 224 + { } 225 + }; 226 + MODULE_DEVICE_TABLE(of, apple_pcie_of_match); 227 + 228 + static struct platform_driver apple_pcie_driver = { 229 + .probe = pci_host_common_probe, 230 + .driver = { 231 + .name = "pcie-apple", 232 + .of_match_table = apple_pcie_of_match, 233 + .suppress_bind_attrs = true, 234 + }, 235 + }; 236 + module_platform_driver(apple_pcie_driver); 237 + 238 + MODULE_LICENSE("GPL v2");