diff --git a/Documentation/devicetree/bindings/iommu/apple,dart.yaml b/Documentation/devicetree/bindings/iommu/apple,dart.yaml index 38a24d0e27c36..0bee5d822fb21 100644 --- a/Documentation/devicetree/bindings/iommu/apple,dart.yaml +++ b/Documentation/devicetree/bindings/iommu/apple,dart.yaml @@ -146,6 +146,16 @@ properties: before the domain powers off, and the slice is enabled again when it powers up. Only the J713 internal display DARTs use it. + apple,manual-availability: + type: boolean + description: > + The DART's registers answer only once its client, a PCIe root complex, + is up. The client's driver makes the DART available after bringing the + root complex up, and the DART probes then. The root complex lists the DART + in its post-init-providers property as well as in iommu-map: otherwise + fw_devlink.strict=1 makes the root complex wait for a DART that waits for + it. + apple,tunable: $ref: /schemas/types.yaml#/definitions/uint32-matrix items: diff --git a/Documentation/devicetree/bindings/pci/apple,pcie.yaml b/Documentation/devicetree/bindings/pci/apple,pcie.yaml index 11f3cfe160c6c..78f154ce2edcd 100644 --- a/Documentation/devicetree/bindings/pci/apple,pcie.yaml +++ b/Documentation/devicetree/bindings/pci/apple,pcie.yaml @@ -46,6 +46,19 @@ $defs: - description: Bits to set description: Register initialization tuples for one controller region. + t6050-tunable: + $ref: /schemas/types.yaml#/definitions/uint32-matrix + items: + items: + - description: Register offset in the named window + - description: Access width in bytes + const: 4 + - description: Mask of the bits to replace + - description: Value of those bits + description: + A T6050 host tunable list, copied by the boot loader from the Apple + device tree. + properties: compatible: oneOf: @@ -99,11 +112,11 @@ properties: reg: minItems: 3 - maxItems: 14 + maxItems: 20 reg-names: minItems: 3 - maxItems: 14 + maxItems: 20 ranges: minItems: 2 @@ -134,6 +147,13 @@ properties: iommu-map: true iommu-map-mask: true + post-init-providers: + description: + The DART of a host whose iommu-map names a DART marked + apple,manual-availability, so that fw_devlink does not make the host wait + for a DART that probes only after the host has brought up its port. + $ref: /schemas/types.yaml#/definitions/phandle-array + power-domains: description: The power domains the controller needs. Internal controllers on some @@ -175,6 +195,30 @@ properties: $ref: '#/$defs/tunable' description: Initialization tuples for the oe-fabric register region. + apple,tunable-axi2af: + $ref: '#/$defs/t6050-tunable' + description: T6050 tuples for the axi2af window, applied before a train. + + apple,tunable-cio3pllcore: + $ref: '#/$defs/t6050-tunable' + description: T6050 tuples for the CIO3 PLL core, in the pcieclkgen window. + + apple,tunable-pcieclkgen: + $ref: '#/$defs/t6050-tunable' + description: T6050 tuples for the pcieclkgen window. + + apple,tunable-phy: + $ref: '#/$defs/t6050-tunable' + description: T6050 tuples for the phy-window window. + + apple,tunable-phy-ip-pll: + $ref: '#/$defs/t6050-tunable' + description: T6050 PHY PLL tuples for the phy-ip window. + + apple,tunable-phy-ip-auspma: + $ref: '#/$defs/t6050-tunable' + description: T6050 PHY lane tuples for the phy-ip window. + apple,piodma: $ref: /schemas/types.yaml#/definitions/phandle description: @@ -201,7 +245,9 @@ patternProperties: description: The boot loader trained this port's link and handed it over up. The driver adopts the link as it is instead of resetting the port, so - the port needs no PERST# line. + the port needs no PERST# line. A T6050 port that also names + pwren-gpios may be left untrained; the OS then trains it first and + adopts it the same way. apple,tunable: $ref: '#/$defs/tunable' @@ -249,15 +295,18 @@ allOf: properties: reg: minItems: 5 - maxItems: 14 + maxItems: 20 reg-names: description: config and rc, then port, phy and intr2axi for each - bootloader-trained root port 00:.0. + adopted root port 00:.0. A host with a port the OS trains + itself (pwren-gpios) also names the shared PHY (phy-shared), its + common block (phy-common) and the windows its tunable lists + apply to (axi2af, pcieclkgen, phy-window, phy-ip). minItems: 5 - maxItems: 14 + maxItems: 20 items: - pattern: "^(config|rc|(port|phy|intr2axi)[0-3])$" + pattern: "^(config|rc|(port|phy|intr2axi)[0-3]|phy-shared|phy-common|axi2af|pcieclkgen|phy-window|phy-ip)$" interrupts: maxItems: 4 interrupt-names: @@ -277,11 +326,24 @@ allOf: description: The PERST# line of a port whose endpoint needs a root-port reset after the bootloader trained the link. Left as the - bootloader set it until that reset. - pwren-gpios: false + bootloader set it until that reset, or until the OS trains + the port itself. + pwren-gpios: + description: + The endpoint's power line. A port that names it may be left + untrained by the bootloader; the OS then powers the endpoint + and trains the link itself (the host then needs phy-shared + and phy-common), and adopts it as a bootloader-trained one. required: - apple,link-trained-by-bootloader else: + properties: + apple,tunable-axi2af: false + apple,tunable-cio3pllcore: false + apple,tunable-pcieclkgen: false + apple,tunable-phy: false + apple,tunable-phy-ip-pll: false + apple,tunable-phy-ip-auspma: false patternProperties: "^pci@": properties: @@ -443,14 +505,15 @@ allOf: properties: reg: minItems: 5 - maxItems: 14 + maxItems: 20 reg-names: minItems: 5 - maxItems: 14 + maxItems: 20 items: enum: [config, rc, port0, phy0, intr2axi0, port1, phy1, intr2axi1, port2, phy2, intr2axi2, port3, phy3, - intr2axi3] + intr2axi3, phy-shared, phy-common, axi2af, + pcieclkgen, phy-window, phy-ip] interrupts: maxItems: 4 else: diff --git a/drivers/gpio/gpio-macsmc.c b/drivers/gpio/gpio-macsmc.c index 6a26f900bba28..434ed549d4c1e 100644 --- a/drivers/gpio/gpio-macsmc.c +++ b/drivers/gpio/gpio-macsmc.c @@ -41,7 +41,8 @@ * pKW4("gP13", 0x00800000) (apple_t8132_managed_radio()). The firmware * function preserves the template's upper 16 bits and inserts the level into * its low 16 bits. This does not establish generic CMD_OUTPUT semantics, a - * pin-mode command, or a readback operation for this line. + * pin-mode command, or a readback operation for this line. The J716s N1's + * power, /arm-io/apcie0/pci-bridge0 function-reset, is the same descriptor. */ #define MACSMC_J713_RADIO_REG_ON_GPIO 0x13 #define MACSMC_J713_RADIO_REG_ON_TEMPLATE 0x00800000 @@ -526,7 +527,8 @@ static int macsmc_gpio_probe(struct platform_device *pdev) smcgp->base_key = data ? data->base_key : _SMC_KEY("gP\0\0"); smcgp->j700_pcio = of_machine_is_compatible("apple,j700") && device_is_compatible(&pdev->dev, "apple,smc-gpio"); - smcgp->j713 = apple_t8132_managed_radio(); + smcgp->j713 = apple_t8132_managed_radio() || + of_machine_is_compatible("apple,j716s"); mutex_init(&smcgp->j713_reg_on_lock); ret = macsmc_gpio_parse_templates(smcgp); if (ret) diff --git a/drivers/iommu/apple-dart.c b/drivers/iommu/apple-dart.c index e9929282d0f78..a91528c3b7575 100644 --- a/drivers/iommu/apple-dart.c +++ b/drivers/iommu/apple-dart.c @@ -4372,6 +4372,43 @@ static int apple_dart_parse_own_setup(struct apple_dart *dart, struct resource * return 0; } +struct apple_dart_available { + struct list_head list; + struct device_node *np; +}; + +static DEFINE_MUTEX(apple_dart_available_lock); +static LIST_HEAD(apple_dart_available_list); + +void apple_dart_make_available(struct device_node *np) +{ + struct apple_dart_available *a; + + guard(mutex)(&apple_dart_available_lock); + list_for_each_entry(a, &apple_dart_available_list, list) + if (a->np == np) + return; + a = kzalloc_obj(*a); + if (!a) + return; + a->np = of_node_get(np); + list_add(&a->list, &apple_dart_available_list); +} +EXPORT_SYMBOL_GPL(apple_dart_make_available); + +static bool apple_dart_is_available(struct device_node *np) +{ + struct apple_dart_available *a; + + if (!of_property_read_bool(np, "apple,manual-availability")) + return true; + guard(mutex)(&apple_dart_available_lock); + list_for_each_entry(a, &apple_dart_available_list, list) + if (a->np == np) + return true; + return false; +} + static int apple_dart_probe(struct platform_device *pdev) { int ret, sid; @@ -4475,6 +4512,10 @@ static int apple_dart_probe(struct platform_device *pdev) } } + /* Its registers answer only once its client is up */ + if (!apple_dart_is_available(dev->of_node)) + return dev_err_probe(dev, -EPROBE_DEFER, "waiting for its client\n"); + dart = kzalloc(sizeof(*dart), GFP_KERNEL); if (!dart) return -ENOMEM; diff --git a/drivers/pci/controller/pcie-apple.c b/drivers/pci/controller/pcie-apple.c index 67d4f3050e06c..b0f9cc67858f5 100644 --- a/drivers/pci/controller/pcie-apple.c +++ b/drivers/pci/controller/pcie-apple.c @@ -94,11 +94,19 @@ MODULE_PARM_DESC(link_up_timeout, "PCIe link training timeout in milliseconds"); #define PHY_LANE_CFG_REFBUF_OFF BIT(4) #define PHY_LANE_CFG_REFCLKEN (BIT(9) | BIT(10)) #define PHY_LANE_CFG_REFCLKCGEN (BIT(30) | BIT(31)) +/* Bit 31 of the pair: the only one a trace of macOS shows cleared, after PERST# is asserted */ +#define PHY_LANE_CFG_REFCLKCGEN_B31 BIT(31) #define PHY_LANE_CTL 0x00004 #define PHY_LANE_CTL_CFGACC BIT(15) #define PORT_LTSSMCTL 0x00080 #define PORT_LTSSMCTL_START BIT(0) +/* T6050: a trace of macOS shows 0x11 written here to send PME_Turn_Off */ +#define PORT_PME_TURN_OFF 0x00088 +/* Reads back 0 within a few reads of the write (the trace reads 0x1, then 0x10) */ +#define PORT_PME_TURN_OFF_SEND BIT(0) +/* Written with bit 0 and still set afterwards; meaning not known */ +#define PORT_PME_TURN_OFF_BIT4 BIT(4) #define PORT_INTSTAT 0x00100 #define PORT_INT_TUNNEL_ERR 31 #define PORT_INT_CPL_TIMEOUT 23 @@ -127,6 +135,8 @@ MODULE_PARM_DESC(link_up_timeout, "PCIe link training timeout in milliseconds"); #define PORT_LINKSTS 0x00208 #define PORT_LINKSTS_UP BIT(0) #define PORT_LINKSTS_BUSY BIT(2) +/* In traces of macOS: 0x9b00134d for a port that reached L2, 0x9b001001 for one that did not */ +#define PORT_LINKSTS_L2 BIT(6) #define PORT_LINKCMDSTS 0x00210 #define PORT_OUTS_NPREQS 0x00284 #define PORT_OUTS_NPREQS_REQ BIT(24) @@ -482,6 +492,7 @@ struct apple_pcie { void __iomem *intr2axi_base; void __iomem *oe_fabric_base; void __iomem *early_cfg; + void __iomem *t6050_phy; /* shared PHY, mapped by a cold train */ struct pci_config_window *cfg; struct apple_tunable *rc_tunable; struct apple_tunable *fabric_tunable; @@ -541,6 +552,8 @@ struct apple_pcie_port { void __iomem *intr2axi; /* adopt_link: this port's */ u8 adopted_bus; /* adopt_link: the secondary bus */ unsigned long adopted_slots; /* adopt_link: RID2SID slots routed (slot == SID) */ + struct gpio_desc *pwren; /* adopt_link: endpoint power, if Linux trained */ + bool powered; /* cold train: pwren raised, port not parked yet */ }; static int apple_pcie_managed_admit(struct apple_pcie_port *port); @@ -1880,7 +1893,8 @@ static int apple_pcie_adopt_link(struct apple_pcie_port *port) return ret; snprintf(name, sizeof(name), "intr2axi%d", port->idx); - port->intr2axi = devm_platform_ioremap_resource_byname(pdev, name); + if (!port->intr2axi) + port->intr2axi = devm_platform_ioremap_resource_byname(pdev, name); if (IS_ERR(port->intr2axi)) return PTR_ERR(port->intr2axi); /* @@ -1889,8 +1903,9 @@ static int apple_pcie_adopt_link(struct apple_pcie_port *port) * controller owns the pad; the line is taken as the bootloader left it, * released, and driven only by the reset. */ - port->perst = devm_fwnode_gpiod_get(pcie->dev, of_fwnode_handle(port->np), "reset", - GPIOD_ASIS, "PERST#"); + if (!port->perst) + port->perst = devm_fwnode_gpiod_get(pcie->dev, of_fwnode_handle(port->np), + "reset", GPIOD_ASIS, "PERST#"); if (IS_ERR(port->perst)) { if (PTR_ERR(port->perst) != -ENOENT) return PTR_ERR(port->perst); @@ -1992,15 +2007,30 @@ static void apple_pcie_t6050_rmw(void __iomem *addr, u32 clear, u32 set) writel((readl(addr) & ~clear) | set, addr); } -static int apple_pcie_t6050_rc_rmw(struct pci_dev *rp, int where, u32 clear, u32 set) +/* The root port's config space through the ECAM window: there is no pci_dev before enumeration */ +static void __iomem *apple_pcie_t6050_dbi(struct apple_pcie_port *port) { - u32 value; - int ret; + return port->pcie->cfg->win + (PCI_DEVFN(port->idx, 0) << 12); +} - ret = pci_read_config_dword(rp, where, &value); - if (!ret) - ret = pci_write_config_dword(rp, where, (value & ~clear) | set); - return pcibios_err_to_errno(ret); +static void apple_pcie_t6050_dbi_rmw(void __iomem *dbi, int where, u32 clear, u32 set) +{ + writel((readl(dbi + where) & ~clear) | set, dbi + where); +} + +static int apple_pcie_t6050_exp_cap(void __iomem *dbi) +{ + u8 cap = readb(dbi + PCI_CAPABILITY_LIST); + int i; + + for (i = 0; cap && i < 48; i++) { + if (cap < 0x40 || (cap & 3)) + return 0; + if (readb(dbi + cap + PCI_CAP_LIST_ID) == PCI_CAP_ID_EXP) + return cap; + cap = readb(dbi + cap + PCI_CAP_LIST_NEXT); + } + return 0; } static int apple_pcie_t6050_wait(struct apple_pcie_port *port, void __iomem *base, @@ -2021,7 +2051,7 @@ static int apple_pcie_t6050_wait(struct apple_pcie_port *port, void __iomem *bas * (offset, 4, mask, value): for the port's registers, or with @rp for the root * port's config space. A list it did not copy is skipped. */ -static int apple_pcie_t6050_tune(struct apple_pcie_port *port, struct pci_dev *rp, +static int apple_pcie_t6050_tune(struct apple_pcie_port *port, void __iomem *dbi, const char *name) { u32 cells[64]; @@ -2037,18 +2067,11 @@ static int apple_pcie_t6050_tune(struct apple_pcie_port *port, struct pci_dev *r return ret; for (i = 0; i < count; i += 4) if (cells[i + 1] != 4 || (cells[i] & 3) || - cells[i] >= (rp ? PCI_CFG_SPACE_EXP_SIZE : SZ_16K)) + cells[i] >= (dbi ? PCI_CFG_SPACE_EXP_SIZE : SZ_16K)) return -EINVAL; - for (i = 0; i < count; i += 4) { - if (!rp) { - apple_pcie_t6050_rmw(port->base + cells[i], cells[i + 2], cells[i + 3]); - continue; - } - ret = apple_pcie_t6050_rc_rmw(rp, cells[i], cells[i + 2], cells[i + 3]); - if (ret) - return ret; - } + for (i = 0; i < count; i += 4) + apple_pcie_t6050_rmw((dbi ?: port->base) + cells[i], cells[i + 2], cells[i + 3]); return 0; } @@ -2063,12 +2086,13 @@ static int apple_pcie_t6050_perst(struct apple_pcie_port *port, bool asserted) } /* Let the link go idle, hold the endpoint in reset and stop the port and its lane clocks */ -static int apple_pcie_t6050_port_disable(struct apple_pcie_port *port, struct pci_dev *rp) +static int apple_pcie_t6050_port_disable(struct apple_pcie_port *port) { + void __iomem *dbi = apple_pcie_t6050_dbi(port); void __iomem *perst = port->base + PORT_T602X_PERST; void __iomem *lane = port->phy + PHY_LANE_CFG; u32 stat; - int ret, err; + int ret; if (!(readl(port->base + PORT_STATUS) & BIT(2))) { ret = apple_pcie_t6050_wait(port, port->base, 0x820, BIT(2), BIT(2), 10000); @@ -2083,12 +2107,12 @@ static int apple_pcie_t6050_port_disable(struct apple_pcie_port *port, struct pc apple_pcie_t6050_rmw(port->base + 0x13c, 0, BIT(8)); /* The link out of L1 */ - ret = read_poll_timeout_atomic(pci_read_config_dword, err, - err || FIELD_GET(RC_T6050_LTSSM_STATE, stat) != + ret = read_poll_timeout_atomic(readl, stat, + FIELD_GET(RC_T6050_LTSSM_STATE, stat) != RC_T6050_LTSSM_L1_IDLE, - 5, 5000, false, rp, RC_T6050_PORT_DEBUG0, &stat); - if (ret || err) - return ret ?: pcibios_err_to_errno(err); + 5, 5000, false, dbi + RC_T6050_PORT_DEBUG0); + if (ret) + return ret; ret = apple_pcie_t6050_wait(port, port->base, 0xa8, BIT(0), BIT(0), 10000); if (ret) @@ -2163,7 +2187,7 @@ static int apple_pcie_t6050_port_enable(struct apple_pcie_port *port) } /* Root complex tunables and a Gen3 x1 link, with the read-only registers opened for it */ -static int apple_pcie_t6050_port_configure(struct apple_pcie_port *port, struct pci_dev *rp) +static int apple_pcie_t6050_port_configure(struct apple_pcie_port *port) { static const char * const shadow[] = { "apple,pcie-rc-gen3-shadow-tunables", @@ -2171,67 +2195,52 @@ static int apple_pcie_t6050_port_configure(struct apple_pcie_port *port, struct "apple,pcie-rc-gen5-shadow-tunables", }; static const u32 shadow_sel[] = { 0, BIT(24), 0 }; - int cap = pci_pcie_cap(rp); - u32 value; + void __iomem *dbi = apple_pcie_t6050_dbi(port); + int cap = apple_pcie_t6050_exp_cap(dbi); u16 ctl2; - int ret, err, i; + int ret, i; if (!cap) return -EIO; - ret = apple_pcie_t6050_rc_rmw(rp, RC_T6050_MISC_CONTROL_1, 0, RC_T6050_DBI_RO_WR_EN); - if (ret) - return ret; - ret = apple_pcie_t6050_tune(port, rp, "apple,pcie-rc-tunables"); - if (ret) - goto lock; - for (i = 0; i < ARRAY_SIZE(shadow); i++) { - ret = apple_pcie_t6050_rc_rmw(rp, RC_T6050_GEN3_RELATED, - RC_T6050_RATE_SHADOW_SEL, shadow_sel[i]) ?: - apple_pcie_t6050_tune(port, rp, shadow[i]); - if (ret) - goto lock; + apple_pcie_t6050_dbi_rmw(dbi, RC_T6050_MISC_CONTROL_1, 0, RC_T6050_DBI_RO_WR_EN); + ret = apple_pcie_t6050_tune(port, dbi, "apple,pcie-rc-tunables"); + for (i = 0; !ret && i < ARRAY_SIZE(shadow); i++) { + apple_pcie_t6050_dbi_rmw(dbi, RC_T6050_GEN3_RELATED, RC_T6050_RATE_SHADOW_SEL, + shadow_sel[i]); + ret = apple_pcie_t6050_tune(port, dbi, shadow[i]); + } + if (!ret) { + apple_pcie_t6050_dbi_rmw(dbi, 0x194, 0, BIT(0)); + if (!(readl(dbi + cap + PCI_EXP_LNKCAP2) & PCI_EXP_LNKCAP2_SLS_8_0GB)) + ret = -EIO; } - ret = apple_pcie_t6050_rc_rmw(rp, 0x194, 0, BIT(0)); - if (ret) - goto lock; - - ret = pcibios_err_to_errno(pci_read_config_dword(rp, cap + PCI_EXP_LNKCAP2, &value)); - if (ret) - goto lock; - if (!(value & PCI_EXP_LNKCAP2_SLS_8_0GB)) { - ret = -EIO; - goto lock; - } - ret = pcibios_err_to_errno(pci_read_config_word(rp, cap + PCI_EXP_LNKCTL2, &ctl2)) ?: - pcibios_err_to_errno(pci_write_config_word(rp, cap + PCI_EXP_LNKCTL2, - (ctl2 & ~PCI_EXP_LNKCTL2_TLS) | - PCI_EXP_LNKCTL2_TLS_8_0GT)) ?: - apple_pcie_t6050_rc_rmw(rp, RC_T6050_PORT_LINK_CTRL, RC_T6050_LINK_MODE, - FIELD_PREP(RC_T6050_LINK_MODE, 1)) ?: - apple_pcie_t6050_rc_rmw(rp, RC_T6050_LINK_WIDTH_SPEED_CTRL, RC_T6050_LINK_WIDTH, - FIELD_PREP(RC_T6050_LINK_WIDTH, 1)) ?: - apple_pcie_t6050_rc_rmw(rp, cap + PCI_EXP_LNKCAP, PCI_EXP_LNKCAP_MLW, - FIELD_PREP(PCI_EXP_LNKCAP_MLW, 1)); -lock: - err = apple_pcie_t6050_rc_rmw(rp, RC_T6050_MISC_CONTROL_1, RC_T6050_DBI_RO_WR_EN, 0); - ret = ret ?: err; + if (!ret) { + ctl2 = readw(dbi + cap + PCI_EXP_LNKCTL2); + writew((ctl2 & ~PCI_EXP_LNKCTL2_TLS) | PCI_EXP_LNKCTL2_TLS_8_0GT, + dbi + cap + PCI_EXP_LNKCTL2); + apple_pcie_t6050_dbi_rmw(dbi, RC_T6050_PORT_LINK_CTRL, RC_T6050_LINK_MODE, + FIELD_PREP(RC_T6050_LINK_MODE, 1)); + apple_pcie_t6050_dbi_rmw(dbi, RC_T6050_LINK_WIDTH_SPEED_CTRL, RC_T6050_LINK_WIDTH, + FIELD_PREP(RC_T6050_LINK_WIDTH, 1)); + apple_pcie_t6050_dbi_rmw(dbi, cap + PCI_EXP_LNKCAP, PCI_EXP_LNKCAP_MLW, + FIELD_PREP(PCI_EXP_LNKCAP_MLW, 1)); + } + apple_pcie_t6050_dbi_rmw(dbi, RC_T6050_MISC_CONTROL_1, RC_T6050_DBI_RO_WR_EN, 0); if (ret) return ret; - ret = apple_pcie_t6050_rc_rmw(rp, cap + PCI_EXP_LNKCTL, 0, PCI_EXP_LNKCTL_LBMIE) ?: - apple_pcie_t6050_rc_rmw(rp, RC_T6050_LINK_WIDTH_SPEED_CTRL, 0, RC_T6050_SPEED_CHANGE); - if (ret) - return ret; + apple_pcie_t6050_dbi_rmw(dbi, cap + PCI_EXP_LNKCTL, 0, PCI_EXP_LNKCTL_LBMIE); + apple_pcie_t6050_dbi_rmw(dbi, RC_T6050_LINK_WIDTH_SPEED_CTRL, 0, RC_T6050_SPEED_CHANGE); writel(PCIEC_INTR2AXI_ENABLE, port->intr2axi + PCIEC_INTR2AXI_CTRL); return 0; } /* Disable, reset, enable and configure an adopted port whose endpoint is held in PERST# */ -static int apple_pcie_t6050_port_restart(struct apple_pcie_port *port, struct pci_dev *rp) +static int apple_pcie_t6050_port_restart(struct apple_pcie_port *port) { - return apple_pcie_t6050_port_disable(port, rp) ?: + return apple_pcie_t6050_port_disable(port) ?: apple_pcie_t6050_port_enable(port) ?: - apple_pcie_t6050_port_configure(port, rp); + apple_pcie_t6050_port_configure(port); } /* Lane clocks, PERST# released, then the LTSSM to link up */ @@ -2261,8 +2270,9 @@ static int apple_pcie_t6050_port_train(struct apple_pcie_port *port) if (ret) return ret; apple_pcie_t6050_rmw(port->base + PORT_LTSSMCTL, 0, PORT_LTSSMCTL_START); + /* Up to 1 s: what m1n1 allows the J716s N1 after its power-up */ ret = apple_pcie_t6050_wait(port, port->base, PORT_LINKSTS, PORT_LINKSTS_UP, - PORT_LINKSTS_UP, 500000); + PORT_LINKSTS_UP, 1000000); if (ret) return ret; @@ -2271,6 +2281,319 @@ static int apple_pcie_t6050_port_train(struct apple_pcie_port *port) return 0; } +/* + * A T6050 port the boot loader left untrained (the J714c's N1 on every boot): the shared + * PCIe PHY clocked and out of reset, the RC running, the endpoint powered (pwren-gpios) + * with PERST# held, then the port enable, configure and train sequences the root-port + * reset uses, and the bus numbers a boot loader leaves. The adopt checks then run as for + * a port trained before Linux. Order and values are m1n1's controller bring-up and the + * kernel's own T6050 port sequence. + */ +#define T6050_PHY_CTRL 0x0 +#define T6050_PHY_CTRL_CLK0REQ BIT(0) +#define T6050_PHY_CTRL_CLK1REQ BIT(1) +#define T6050_PHY_CTRL_CLK0ACK BIT(2) +#define T6050_PHY_CTRL_CLK1ACK BIT(3) +#define T6050_PHY_CTRL_RESET_T8132 BIT(4) +#define T6050_PHY_CTRL_RESET_T8103 BIT(7) +#define T6050_PHY_CTRL_LANES GENMASK(9, 8) +#define T6050_PHY_CFG 0x4 +/* + * Set before the PHY-IP tunables. A trace of macOS bringing the port up shows no + * access to this bit; meaning not known. + */ +#define T6050_PHY_CFG_BIT0 BIT(0) +/* + * Set after the PHY-IP tunables. A trace of macOS shows it set too, after which + * T6050_PHY_STAT_BIT0 rises (the trace reads 0x10 seven times, then 0x11); meaning + * not known. + */ +#define T6050_PHY_CFG_BIT4 BIT(4) +#define T6050_PHY_STAT 0x8 +/* Rises once T6050_PHY_CFG_BIT4 is set; polled by macOS and by m1n1; meaning not known */ +#define T6050_PHY_STAT_BIT0 BIT(0) +#define T6050_PHYCMN_CLK 0x0 +#define T6050_PHYCMN_CLK_MODE GENMASK(1, 0) +/* + * Written before CORE_RC_CTL_RUN: a trace of macOS shows 0x140 written here, and m1n1 + * writes the same. Neither bit's meaning is known. + */ +#define CORE_RC_T6050_PRE_RUN 0x54 +#define CORE_RC_T6050_PRE_RUN_BIT6 BIT(6) +#define CORE_RC_T6050_PRE_RUN_BIT8 BIT(8) + +static int apple_pcie_t6050_phy_wait(struct apple_pcie *pcie, u32 mask, u32 value) +{ + u32 v; + int ret; + + ret = readl_poll_timeout(pcie->t6050_phy + T6050_PHY_CTRL, v, (v & mask) == value, + 10, 50000); + if (ret) + dev_err(pcie->dev, "shared PHY: %#x, waiting for %#x/%#x\n", v, value, mask); + return ret; +} + +/* + * A host tunable list (apple,tunable-, groups of (offset, 4, mask, value)) applied + * to the host window @win. The window may contain windows mapped elsewhere (the PHY + * window holds the port and shared PHYs), so it is mapped without claiming it. + */ +static int apple_pcie_t6050_tune_host(struct apple_pcie *pcie, const char *name, + const char *win) +{ + struct platform_device *pdev = to_platform_device(pcie->dev); + struct device_node *np = pcie->dev->of_node; + struct resource *res; + void __iomem *base; + char prop[48]; + u32 *cells; + int count, i, ret; + + snprintf(prop, sizeof(prop), "apple,tunable-%s", name); + if (!of_property_present(np, prop)) + return 0; + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, win); + count = of_property_count_u32_elems(np, prop); + if (!res || count <= 0 || count % 4) + return -EINVAL; + cells = kmalloc_array(count, sizeof(*cells), GFP_KERNEL); + if (!cells) + return -ENOMEM; + ret = of_property_read_u32_array(np, prop, cells, count); + for (i = 0; !ret && i < count; i += 4) + if (cells[i + 1] != 4 || (cells[i] & 3) || cells[i] > resource_size(res) - 4) + ret = -EINVAL; + if (!ret) { + base = ioremap(res->start, resource_size(res)); + if (base) { + for (i = 0; i < count; i += 4) + apple_pcie_t6050_rmw(base + cells[i], cells[i + 2], cells[i + 3]); + iounmap(base); + } else { + ret = -ENOMEM; + } + } + kfree(cells); + if (ret) + dev_err(pcie->dev, "%s: %d\n", prop, ret); + return ret; +} + +static int apple_pcie_t6050_shared_phy_on(struct apple_pcie *pcie) +{ + struct platform_device *pdev = to_platform_device(pcie->dev); + const u32 up = T6050_PHY_CTRL_CLK0ACK | T6050_PHY_CTRL_CLK1ACK | T6050_PHY_CTRL_LANES; + void __iomem *phy, *cmn; + u32 v; + int ret; + + if (!pcie->t6050_phy) { + phy = devm_platform_ioremap_resource_byname(pdev, "phy-shared"); + if (IS_ERR(phy)) + return PTR_ERR(phy); + pcie->t6050_phy = phy; + } + phy = pcie->t6050_phy; + if ((readl(phy + T6050_PHY_CTRL) & (up | T6050_PHY_CTRL_RESET_T8132 | + T6050_PHY_CTRL_RESET_T8103)) == up) + return 0; + cmn = devm_platform_ioremap_resource_byname(pdev, "phy-common"); + if (IS_ERR(cmn)) + return PTR_ERR(cmn); + + /* Fabric, clock generator and PHY tunables, then the PHY clocks (m1n1's order) */ + ret = apple_pcie_t6050_tune_host(pcie, "axi2af", "axi2af") ?: + apple_pcie_t6050_tune_host(pcie, "cio3pllcore", "pcieclkgen") ?: + apple_pcie_t6050_tune_host(pcie, "pcieclkgen", "pcieclkgen") ?: + apple_pcie_t6050_tune_host(pcie, "phy", "phy-window"); + if (ret) + return ret; + apple_pcie_t6050_rmw(phy + T6050_PHY_CTRL, 0, T6050_PHY_CTRL_CLK0REQ); + ret = apple_pcie_t6050_phy_wait(pcie, T6050_PHY_CTRL_CLK0ACK, T6050_PHY_CTRL_CLK0ACK); + if (ret) + return ret; + apple_pcie_t6050_rmw(phy + T6050_PHY_CTRL, 0, T6050_PHY_CTRL_CLK1REQ); + ret = apple_pcie_t6050_phy_wait(pcie, T6050_PHY_CTRL_CLK1ACK, T6050_PHY_CTRL_CLK1ACK); + if (ret) + return ret; + apple_pcie_t6050_rmw(phy + T6050_PHY_CTRL, T6050_PHY_CTRL_RESET_T8132, 0); + ret = apple_pcie_t6050_phy_wait(pcie, T6050_PHY_CTRL_RESET_T8132, 0); + if (ret) + return ret; + apple_pcie_t6050_rmw(phy + T6050_PHY_CTRL, T6050_PHY_CTRL_RESET_T8103, 0); + ret = apple_pcie_t6050_phy_wait(pcie, T6050_PHY_CTRL_RESET_T8103, 0); + if (ret) + return ret; + apple_pcie_t6050_rmw(phy + T6050_PHY_CFG, 0, T6050_PHY_CFG_BIT0); + ret = apple_pcie_t6050_tune_host(pcie, "phy-ip-pll", "phy-ip") ?: + apple_pcie_t6050_tune_host(pcie, "phy-ip-auspma", "phy-ip"); + if (ret) + return ret; + apple_pcie_t6050_rmw(phy + T6050_PHY_CFG, 0, T6050_PHY_CFG_BIT4); + apple_pcie_t6050_rmw(cmn + T6050_PHYCMN_CLK, T6050_PHYCMN_CLK_MODE, + FIELD_PREP(T6050_PHYCMN_CLK_MODE, 1)); + if (readl_poll_timeout(phy + T6050_PHY_STAT, v, v & T6050_PHY_STAT_BIT0, 10, 250000)) + dev_info(pcie->dev, "shared PHY: clock status %#x\n", v); + apple_pcie_t6050_rmw(phy + T6050_PHY_CTRL, 0, T6050_PHY_CTRL_LANES); + return 0; +} + +/* The port's DART answers once the root complex is up (apple,manual-availability) */ +static void apple_pcie_dart_available(struct apple_pcie_port *port) +{ + struct of_phandle_args iommu = {}; + + if (of_map_iommu_id(port->pcie->dev->of_node, port->adopted_bus << 8, &iommu) || + !iommu.np) + return; + apple_dart_make_available(iommu.np); + of_node_put(iommu.np); +} + +static int apple_pcie_t6050_cold_train(struct apple_pcie_port *port) +{ + struct apple_pcie *pcie = port->pcie; + struct platform_device *pdev = to_platform_device(pcie->dev); + struct gpio_desc *pwren; + void __iomem *dbi; + char name[16]; + u32 v, bus; + int ret; + + bus = pcie->cfg->busr.start + 1 + port->idx; + if (bus > pcie->cfg->busr.end) + return -EINVAL; + + /* + * Both lines before the first register write: a line that is not there yet (the SMC + * GPIO bank probes late) defers the probe with the hardware untouched. The endpoint + * is held off from here. A restart resets the SoC but not the endpoint: after one + * without a shutdown that powered it off, the J716s N1 still runs its firmware, and + * only a power cycle of 250 ms (what m1n1 measured for a warm N1) brings it back in + * its ROM personality. + */ + if (!port->perst) + port->perst = devm_fwnode_gpiod_get(pcie->dev, of_fwnode_handle(port->np), + "reset", GPIOD_ASIS, "PERST#"); + if (IS_ERR(port->perst)) + return PTR_ERR(port->perst); + pwren = devm_fwnode_gpiod_get(pcie->dev, of_fwnode_handle(port->np), "pwren", + GPIOD_OUT_LOW, "PWREN"); + if (IS_ERR(pwren)) + return PTR_ERR(pwren); + port->pwren = pwren; + + ret = apple_pcie_t6050_shared_phy_on(pcie); + if (ret) + return ret; + if (!(readl(pcie->base + CORE_RC_CTL) & CORE_RC_CTL_RUN)) { + writel(CORE_RC_T6050_PRE_RUN_BIT6 | CORE_RC_T6050_PRE_RUN_BIT8, + pcie->base + CORE_RC_T6050_PRE_RUN); + writel(CORE_RC_CTL_RUN, pcie->base + CORE_RC_CTL); + } + ret = readl_poll_timeout(pcie->base + CORE_RC_STAT, v, v & CORE_RC_STAT_READY, 10, 250000); + if (ret) + return ret; + + snprintf(name, sizeof(name), "intr2axi%d", port->idx); + if (!port->intr2axi) + port->intr2axi = devm_platform_ioremap_resource_byname(pdev, name); + if (IS_ERR(port->intr2axi)) + return PTR_ERR(port->intr2axi); + ret = apple_pcie_t6050_perst(port, true); + if (ret) + return ret; + + /* The endpoint held, then powered: the boot loader leaves it off */ + msleep(250); + /* From here the endpoint may be on, and a failure has to park the port */ + port->powered = true; + ret = gpiod_set_value_cansleep(pwren, 1); + if (ret) + return ret; + msleep(150); + + ret = apple_pcie_t6050_port_enable(port) ?: + apple_pcie_t6050_port_configure(port) ?: + apple_pcie_t6050_port_train(port); + if (ret) + return ret; + + /* Primary bus 0, the port's secondary and subordinate bus, as a boot loader leaves them */ + dbi = apple_pcie_t6050_dbi(port); + v = readl(dbi + PCI_PRIMARY_BUS) & 0xff000000; + writel(v | (bus << 8) | (bus << 16), dbi + PCI_PRIMARY_BUS); + dev_info(pcie->dev, "port %d: trained by Linux, the boot loader left it down\n", port->idx); + return 0; +} + +/* + * PME_Turn_Off, then wait for L2: the clock gate disabled, the message sent, and the + * link reported in L2. Both waits are made even when the first fails (the N1 may + * clear the send bit and never reach L2, as under macOS); the first error is returned. + */ +static int apple_pcie_t6050_port_l2(struct apple_pcie_port *port) +{ + int ret, ret2; + + apple_pcie_port_writel(port, apple_pcie_port_readl(port, PORT_APPCLK) | PORT_APPCLK_CGDIS, + PORT_APPCLK); + apple_pcie_port_writel(port, PORT_PME_TURN_OFF_SEND | PORT_PME_TURN_OFF_BIT4, + PORT_PME_TURN_OFF); + ret = apple_pcie_t6050_wait(port, port->base, PORT_PME_TURN_OFF, PORT_PME_TURN_OFF_SEND, + 0, 500000); + ret2 = apple_pcie_t6050_wait(port, port->base, PORT_LINKSTS, PORT_LINKSTS_L2, + PORT_LINKSTS_L2, 500000); + return ret ?: ret2; +} + +/* + * Put a T6050 port back the way the boot stage leaves it on a cold start: link to L2 + * (only for a link that is up: PME_Turn_Off has no one to answer on a link that never + * trained), PERST# asserted, lane clock request off, the port disabled and its clocks + * stopped, and the endpoint's power off if Linux raised it. Errors are ignored: each + * step is best effort and the rest still has to run. Config space is not touched. + */ +static void apple_pcie_t6050_port_park(struct apple_pcie_port *port, bool link_up) +{ + if (link_up) + apple_pcie_t6050_port_l2(port); + WRITE_ONCE(port->link_failed, true); + apple_pcie_t6050_perst(port, true); + writel(readl(port->phy) & ~PHY_LANE_CFG_REFCLKCGEN_B31, port->phy); + apple_pcie_t6050_port_disable(port); + /* An endpoint Linux powered goes off, so the next boot finds it cold */ + if (port->pwren) + gpiod_set_value_cansleep(port->pwren, 0); + port->powered = false; +} + +/* + * A port this driver cold-trained whose setup then failed: its link may be up and its + * endpoint is powered, and nothing else will stop them (the shutdown hook walks + * pcie->ports, which no longer holds this port). Called after apple_pcie_port_teardown(), + * which still needs the port's registers clocked. A failure before the endpoint was + * powered left nothing running: pwren was requested low and no port register was written. + */ +static void apple_pcie_t6050_cold_unwind(struct apple_pcie_port *port) +{ + bool link_up; + + if (!port->powered) + return; + link_up = apple_pcie_port_readl(port, PORT_LINKSTS) & PORT_LINKSTS_UP; + /* The bus numbers cold_train wrote, as the shutdown hook clears them */ + if (link_up) { + void __iomem *dbi = apple_pcie_t6050_dbi(port); + + writel(readl(dbi + PCI_PRIMARY_BUS) & 0xff0000ff, dbi + PCI_PRIMARY_BUS); + } + apple_pcie_t6050_port_park(port, link_up); + dev_info(port->pcie->dev, "port %d parked after a failed setup, its endpoint powered off\n", + port->idx); +} + /* * Root-port reset of an adopted T6050 port, for an endpoint that must come back on a * retrained link (the N1 after its ROM boot): PME_Turn_Off to L2, PERST# asserted, the @@ -2307,18 +2630,15 @@ static int apple_pcie_t6050_reset_root_port(struct pci_host_bridge *bridge, if (ret) goto out; /* PME_Turn_Off, then L2 */ - apple_pcie_port_writel(port, apple_pcie_port_readl(port, PORT_APPCLK) | 0x100, PORT_APPCLK); - apple_pcie_port_writel(port, 0x11, 0x88); - ret = apple_pcie_t6050_wait(port, port->base, 0x88, 1, 0, 500000) ?: - apple_pcie_t6050_wait(port, port->base, PORT_LINKSTS, BIT(6), BIT(6), 500000); + ret = apple_pcie_t6050_port_l2(port); if (ret) goto out; WRITE_ONCE(port->link_failed, true); ret = apple_pcie_t6050_perst(port, true); if (ret) goto out; - writel(readl(port->phy) & ~BIT(31), port->phy); - ret = apple_pcie_t6050_port_restart(port, rp); + writel(readl(port->phy) & ~PHY_LANE_CFG_REFCLKCGEN_B31, port->phy); + ret = apple_pcie_t6050_port_restart(port); if (ret) goto out; @@ -2640,8 +2960,15 @@ static int apple_pcie_setup_port(struct apple_pcie *pcie, } } else if (pcie->hw->adopt_link) { ret = apple_pcie_adopt_link(port); + /* A port left down that names its endpoint's power line is trained here */ + if (ret == -ENOLINK && of_property_present(np, "pwren-gpios")) { + ret = apple_pcie_t6050_cold_train(port); + if (!ret) + ret = apple_pcie_adopt_link(port); + } if (ret) goto err_teardown; + apple_pcie_dart_available(port); } else { if (pcie->hw->retain_clocks) rmw_set(PORT_APPCLK_EN, port->base + PORT_APPCLK); @@ -2792,6 +3119,7 @@ static int apple_pcie_setup_port(struct apple_pcie *pcie, err_teardown: apple_pcie_port_teardown(port); + apple_pcie_t6050_cold_unwind(port); return ret; } @@ -3665,11 +3993,24 @@ static int apple_pcie_tunnel_pci_notify(struct notifier_block *nb, switch (action) { case BUS_NOTIFY_ADD_DEVICE: + /* + * T6050 adopted ports: no ASPM at all. On the J714c the N1 in its ROM + * personality (no firmware yet) takes the SoC down 20-40 s after the link + * first idles into L1; pcie_aspm=off runs clean. + */ + if (pcie->hw->adopt_link) { + if (pci_pcie_type(pdev) == PCI_EXP_TYPE_ROOT_PORT) + pcie_aspm_remove_cap(pdev, PCI_EXP_LNKCAP_ASPM_L0S | + PCI_EXP_LNKCAP_ASPM_L1); + break; + } pm_runtime_get_noresume(&pdev->dev); if (pci_pcie_type(pdev) == PCI_EXP_TYPE_ROOT_PORT) pcie_aspm_remove_cap(pdev, PCI_EXP_LNKCAP_ASPM_L1); break; case BUS_NOTIFY_DEL_DEVICE: + if (pcie->hw->adopt_link) + break; pm_runtime_put_noidle(&pdev->dev); break; } @@ -4000,8 +4341,7 @@ static int apple_pcie_probe(struct platform_device *pdev) /* A root port 00:.0 on bus 0, n a GP port (0-3) */ if (of_property_read_u32(child, "reg", ®) || reg & ~0xf800 || (reg >> 11) > 3 || - !of_property_read_bool(child, "apple,link-trained-by-bootloader") || - of_property_present(child, "pwren-gpios")) + !of_property_read_bool(child, "apple,link-trained-by-bootloader")) return -EINVAL; /* * A port whose endpoint needs a root-port reset names its PERST# @@ -4112,7 +4452,7 @@ static int apple_pcie_probe(struct platform_device *pdev) } /* Every function is added after this and removed before devres runs. */ - if (pcie->tunnel_cold_init) { + if (pcie->tunnel_cold_init || pcie->hw->adopt_link) { pcie->tunnel_pci_nb.notifier_call = apple_pcie_tunnel_pci_notify; ret = bus_register_notifier(&pci_bus_type, &pcie->tunnel_pci_nb); if (ret) @@ -4667,6 +5007,41 @@ static void apple_pcie_remove(struct platform_device *pdev) pci_unlock_rescan_remove(); } +/* + * A restart with an adopted port's link alive never reaches m1n1 on the J714c (hard + * reset needed). Put each adopted port back the way the boot stage leaves it on a + * cold start: bridge decoding off, link to L2, PERST# asserted, port and lane clocks + * stopped (the first half of apple_pcie_t6050_reset_root_port, without the restart). + */ +static void apple_pcie_shutdown(struct platform_device *pdev) +{ + struct pci_host_bridge *bridge = platform_get_drvdata(pdev); + struct apple_pcie *pcie = pci_host_bridge_priv(bridge); + struct apple_pcie_port *port; + struct pci_dev *rp; + u32 value; + + if (!pcie->hw->adopt_link) + return; + mutex_lock(&pcie->lock); + list_for_each_entry(port, &pcie->ports, entry) { + if (!port->perst || !port->base || !port->phy) + continue; + rp = pci_get_domain_bus_and_slot(pci_domain_nr(bridge->bus), 0, + PCI_DEVFN(port->idx, 0)); + if (!rp) + continue; + pci_write_config_word(rp, PCI_COMMAND, 0); + if (!pci_read_config_dword(rp, PCI_PRIMARY_BUS, &value)) + pci_write_config_dword(rp, PCI_PRIMARY_BUS, value & 0xff0000ff); + apple_pcie_t6050_port_park(port, true); + dev_info(pcie->dev, "port %d held for the restart%s\n", port->idx, + port->pwren ? ", its endpoint powered off" : ""); + pci_dev_put(rp); + } + mutex_unlock(&pcie->lock); +} + static int apple_pcie_mark_power_lost(struct pci_dev *pdev, void *unused) { /* @@ -5097,6 +5472,7 @@ MODULE_DEVICE_TABLE(of, apple_pcie_of_match); static struct platform_driver apple_pcie_driver = { .probe = apple_pcie_probe, .remove = apple_pcie_remove, + .shutdown = apple_pcie_shutdown, .driver = { .name = "pcie-apple", .of_match_table = apple_pcie_of_match, diff --git a/include/linux/soc/apple/dart.h b/include/linux/soc/apple/dart.h index 10d05a7cc042b..01b1c7564673c 100644 --- a/include/linux/soc/apple/dart.h +++ b/include/linux/soc/apple/dart.h @@ -8,6 +8,7 @@ #include struct device; +struct device_node; /* Retained internal-display ownership is qualified only with a built-in * DART provider. Other consumers, including modular DART, keep the APIs below. @@ -161,4 +162,14 @@ static inline int apple_dart_restore_tunnel_state(struct device *dev) { return - static inline int apple_dart_radio_check_idle(struct device *dev) { return -EOPNOTSUPP; } #endif +/* + * A DART marked apple,manual-availability answers only once its client (a + * PCIe root complex) is up. The client makes it available, and it probes then. + */ +#if IS_ENABLED(CONFIG_APPLE_DART) +void apple_dart_make_available(struct device_node *np); +#else +static inline void apple_dart_make_available(struct device_node *np) { } +#endif + #endif