Skip to content

Commit a60af6e

Browse files
committed
Merge branch 'bits/140-pci' into asahi-wip
2 parents b34a1d8 + d425de5 commit a60af6e

2 files changed

Lines changed: 196 additions & 37 deletions

File tree

Documentation/devicetree/bindings/pci/apple,pcie.yaml

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,27 @@ properties:
8282
power-domains:
8383
maxItems: 1
8484

85+
patternProperties:
86+
"^pci@":
87+
$ref: /schemas/pci/pci-bus.yaml#
88+
type: object
89+
description: A single PCI root port
90+
91+
properties:
92+
reg:
93+
maxItems: 1
94+
95+
pwren-gpios:
96+
description: Optional GPIO to power on the device
97+
maxItems: 1
98+
99+
required:
100+
- reset-gpios
101+
- interrupt-controller
102+
- "#interrupt-cells"
103+
- interrupt-map-mask
104+
- interrupt-map
105+
85106
required:
86107
- compatible
87108
- reg
@@ -161,34 +182,58 @@ examples:
161182
pinctrl-0 = <&pcie_pins>;
162183
pinctrl-names = "default";
163184
164-
pci@0,0 {
185+
port00: pci@0,0 {
165186
device_type = "pci";
166187
reg = <0x0 0x0 0x0 0x0 0x0>;
167188
reset-gpios = <&pinctrl_ap 152 0>;
168189
169190
#address-cells = <3>;
170191
#size-cells = <2>;
171192
ranges;
193+
194+
interrupt-controller;
195+
#interrupt-cells = <1>;
196+
interrupt-map-mask = <0 0 0 7>;
197+
interrupt-map = <0 0 0 1 &port00 0 0 0 0>,
198+
<0 0 0 2 &port00 0 0 0 1>,
199+
<0 0 0 3 &port00 0 0 0 2>,
200+
<0 0 0 4 &port00 0 0 0 3>;
172201
};
173202
174-
pci@1,0 {
203+
port01: pci@1,0 {
175204
device_type = "pci";
176205
reg = <0x800 0x0 0x0 0x0 0x0>;
177206
reset-gpios = <&pinctrl_ap 153 0>;
178207
179208
#address-cells = <3>;
180209
#size-cells = <2>;
181210
ranges;
211+
212+
interrupt-controller;
213+
#interrupt-cells = <1>;
214+
interrupt-map-mask = <0 0 0 7>;
215+
interrupt-map = <0 0 0 1 &port01 0 0 0 0>,
216+
<0 0 0 2 &port01 0 0 0 1>,
217+
<0 0 0 3 &port01 0 0 0 2>,
218+
<0 0 0 4 &port01 0 0 0 3>;
182219
};
183220
184-
pci@2,0 {
221+
port02: pci@2,0 {
185222
device_type = "pci";
186223
reg = <0x1000 0x0 0x0 0x0 0x0>;
187224
reset-gpios = <&pinctrl_ap 33 0>;
188225
189226
#address-cells = <3>;
190227
#size-cells = <2>;
191228
ranges;
229+
230+
interrupt-controller;
231+
#interrupt-cells = <1>;
232+
interrupt-map-mask = <0 0 0 7>;
233+
interrupt-map = <0 0 0 1 &port02 0 0 0 0>,
234+
<0 0 0 2 &port02 0 0 0 1>,
235+
<0 0 0 3 &port02 0 0 0 2>,
236+
<0 0 0 4 &port02 0 0 0 3>;
192237
};
193238
};
194239
};

drivers/pci/controller/pcie-apple.c

Lines changed: 148 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@
3333

3434
#include "pci-host-common.h"
3535

36+
static int link_up_timeout = 500;
37+
module_param(link_up_timeout, int, 0644);
38+
MODULE_PARM_DESC(link_up_timeout, "PCIe link training timeout in milliseconds");
39+
3640
/* T8103 (original M1) and related SoCs */
3741
#define CORE_RC_PHYIF_CTL 0x00024
3842
#define CORE_RC_PHYIF_CTL_RUN BIT(0)
@@ -554,22 +558,105 @@ static u32 apple_pcie_rid2sid_write(struct apple_pcie_port *port,
554558
return readl_relaxed(port_rid2sid_addr(port, idx));
555559
}
556560

561+
static int apple_pcie_setup_link(struct apple_pcie *pcie,
562+
struct apple_pcie_port *port,
563+
struct device_node *np)
564+
{
565+
#define MAX_AUX_PERST 3
566+
struct gpio_desc *aux_reset[MAX_AUX_PERST] = { NULL };
567+
u32 num_aux_resets = 0;
568+
struct gpio_desc *reset, *pwren = NULL;
569+
u32 stat;
570+
int ret;
571+
572+
/*
573+
* Assert PERST# and configure the pin as output.
574+
* The Aquantia AQC113 10GB nic used desktop macs is sensitive to
575+
* deasserting it without prior clock setup.
576+
* Observed on M1 Max/Ultra Mac Studios under m1n1's hypervisor.
577+
*/
578+
reset = devm_fwnode_gpiod_get(pcie->dev, of_fwnode_handle(np), "reset",
579+
GPIOD_OUT_HIGH, "PERST#");
580+
if (IS_ERR(reset))
581+
return PTR_ERR(reset);
582+
// HACK: use additional "reset-gpios" until pci-pwrctrl gains PERST# support.
583+
for (u32 idx = 0; idx < MAX_AUX_PERST; idx++) {
584+
aux_reset[idx] = devm_fwnode_gpiod_get_index(pcie->dev,
585+
of_fwnode_handle(np),
586+
"reset", idx + 1,
587+
GPIOD_OUT_HIGH,
588+
"PERST#");
589+
if (IS_ERR(aux_reset[idx])) {
590+
if (PTR_ERR(aux_reset[idx]) == -ENOENT)
591+
break;
592+
else
593+
return PTR_ERR(aux_reset[idx]);
594+
}
595+
num_aux_resets++;
596+
}
597+
dev_info(pcie->dev, "Using %u auxiliary PERST#\n", num_aux_resets);
598+
599+
pwren = devm_fwnode_gpiod_get(pcie->dev, of_fwnode_handle(np), "pwren",
600+
GPIOD_ASIS, "PWREN");
601+
if (IS_ERR(pwren)) {
602+
if (PTR_ERR(pwren) == -ENOENT)
603+
pwren = NULL;
604+
else
605+
return PTR_ERR(pwren);
606+
}
607+
608+
rmw_set(PORT_APPCLK_EN, port->base + PORT_APPCLK);
609+
610+
/* Assert PERST# before setting up the clock */
611+
gpiod_set_value_cansleep(reset, 1);
612+
for (u32 idx = 0; idx < num_aux_resets; idx++)
613+
gpiod_set_value_cansleep(aux_reset[idx], 1);
614+
615+
/* Power on the device if required */
616+
gpiod_set_value_cansleep(pwren, 1);
617+
618+
ret = apple_pcie_setup_refclk(pcie, port);
619+
if (ret < 0)
620+
return ret;
621+
622+
/*
623+
* The minimal Tperst-clk value is 100us (PCIe CEM r5.0, 2.9.2)
624+
* If powering up, the minimal Tpvperl is 100ms
625+
*/
626+
if (pwren)
627+
msleep(100);
628+
else
629+
usleep_range(100, 200);
630+
631+
/* Deassert PERST# */
632+
rmw_set(PORT_PERST_OFF, port->base + pcie->hw->port_perst);
633+
gpiod_set_value_cansleep(reset, 0);
634+
for (u32 idx = 0; idx < num_aux_resets; idx++)
635+
gpiod_set_value_cansleep(aux_reset[idx], 0);
636+
637+
/* Wait for 100ms after PERST# deassertion (PCIe r5.0, 6.6.1) */
638+
msleep(100);
639+
640+
ret = readl_relaxed_poll_timeout(port->base + PORT_STATUS, stat,
641+
stat & PORT_STATUS_READY, 100, 250000);
642+
if (ret < 0) {
643+
dev_err(pcie->dev, "port %pOF ready wait timeout\n", np);
644+
return ret;
645+
}
646+
647+
return 0;
648+
}
649+
557650
static int apple_pcie_setup_port(struct apple_pcie *pcie,
558651
struct device_node *np)
559652
{
560653
struct platform_device *platform = to_platform_device(pcie->dev);
561654
struct apple_pcie_port *port;
562-
struct gpio_desc *reset;
563655
struct resource *res;
564656
char name[16];
565-
u32 stat, idx;
657+
u32 link_stat, idx;
566658
int ret, i;
567659

568-
reset = devm_fwnode_gpiod_get(pcie->dev, of_fwnode_handle(np), "reset",
569-
GPIOD_OUT_LOW, "PERST#");
570-
if (IS_ERR(reset))
571-
return PTR_ERR(reset);
572-
573660
port = devm_kzalloc(pcie->dev, sizeof(*port), GFP_KERNEL);
574661
if (!port)
575662
return -ENOMEM;
@@ -605,30 +692,12 @@ static int apple_pcie_setup_port(struct apple_pcie *pcie,
605692
else
606693
port->phy = pcie->base + CORE_PHY_DEFAULT_BASE(port->idx);
607694

608-
rmw_set(PORT_APPCLK_EN, port->base + PORT_APPCLK);
609-
610-
/* Assert PERST# before setting up the clock */
611-
gpiod_set_value_cansleep(reset, 1);
612-
613-
ret = apple_pcie_setup_refclk(pcie, port);
614-
if (ret < 0)
615-
return ret;
616-
617-
/* The minimal Tperst-clk value is 100us (PCIe CEM r5.0, 2.9.2) */
618-
usleep_range(100, 200);
619-
620-
/* Deassert PERST# */
621-
rmw_set(PORT_PERST_OFF, port->base + pcie->hw->port_perst);
622-
gpiod_set_value_cansleep(reset, 0);
623-
624-
/* Wait for 100ms after PERST# deassertion (PCIe r5.0, 6.6.1) */
625-
msleep(100);
626-
627-
ret = readl_relaxed_poll_timeout(port->base + PORT_STATUS, stat,
628-
stat & PORT_STATUS_READY, 100, 250000);
629-
if (ret < 0) {
630-
dev_err(pcie->dev, "port %pOF ready wait timeout\n", np);
631-
return ret;
695+
/* link might be already brought up by u-boot, skip setup then */
696+
link_stat = readl_relaxed(port->base + PORT_LINKSTS);
697+
if (!(link_stat & PORT_LINKSTS_UP)) {
698+
ret = apple_pcie_setup_link(pcie, port, np);
699+
if (ret)
700+
return ret;
632701
}
633702

634703
if (pcie->hw->port_refclk)
@@ -662,10 +731,21 @@ static int apple_pcie_setup_port(struct apple_pcie *pcie,
662731
ret = apple_pcie_port_register_irqs(port);
663732
WARN_ON(ret);
664733

665-
writel_relaxed(PORT_LTSSMCTL_START, port->base + PORT_LTSSMCTL);
734+
link_stat = readl_relaxed(port->base + PORT_LINKSTS);
735+
if (!(link_stat & PORT_LINKSTS_UP)) {
736+
unsigned long timeout, left;
737+
/* start link training */
738+
writel_relaxed(PORT_LTSSMCTL_START, port->base + PORT_LTSSMCTL);
666739

667-
if (!wait_for_completion_timeout(&pcie->event, HZ / 10))
668-
dev_warn(pcie->dev, "%pOF link didn't come up\n", np);
740+
timeout = link_up_timeout * HZ / 1000;
741+
left = wait_for_completion_timeout(&pcie->event, timeout);
742+
if (!left)
743+
dev_warn(pcie->dev, "%pOF link didn't come up\n", np);
744+
else
745+
dev_info(pcie->dev, "%pOF link up after %ldms\n", np,
746+
(timeout - left) * 1000 / HZ);
747+
748+
}
669749

670750
return 0;
671751
}
@@ -872,12 +952,46 @@ static const struct pci_ecam_ops apple_pcie_cfg_ecam_ops = {
872952
}
873953
};
874954

955+
static int apple_pcie_probe_port(struct device_node *np)
956+
{
957+
struct gpio_desc *gd;
958+
959+
/* check whether the GPPIO pin exists but leave it as is */
960+
gd = fwnode_gpiod_get_index(of_fwnode_handle(np), "reset", 0,
961+
GPIOD_ASIS, "PERST#");
962+
if (IS_ERR(gd))
963+
return PTR_ERR(gd);
964+
965+
gpiod_put(gd);
966+
967+
gd = fwnode_gpiod_get_index(of_fwnode_handle(np), "pwren", 0,
968+
GPIOD_ASIS, "PWREN");
969+
if (IS_ERR(gd)) {
970+
if (PTR_ERR(gd) != -ENOENT)
971+
return PTR_ERR(gd);
972+
} else {
973+
gpiod_put(gd);
974+
}
975+
976+
return 0;
977+
}
978+
875979
static int apple_pcie_probe(struct platform_device *pdev)
876980
{
877981
struct device *dev = &pdev->dev;
982+
struct device_node *of_port;
878983
struct apple_pcie *pcie;
879984
int ret;
880985

986+
/* Check for probe dependencies for all ports first */
987+
for_each_available_child_of_node(dev->of_node, of_port) {
988+
ret = apple_pcie_probe_port(of_port);
989+
if (ret) {
990+
of_node_put(of_port);
991+
return dev_err_probe(dev, ret, "Port %pOF probe fail\n", of_port);
992+
}
993+
}
994+
881995
pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL);
882996
if (!pcie)
883997
return -ENOMEM;

0 commit comments

Comments
 (0)