Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,12 @@ description:

properties:
compatible:
const: qcom,pcie-x1e80100
oneOf:
- const: qcom,pcie-x1e80100
- items:
- enum:
- qcom,glymur-pcie
- const: qcom,pcie-x1e80100

reg:
minItems: 6
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ description:
properties:
compatible:
enum:
- qcom,glymur-qmp-gen4x2-pcie-phy
- qcom,glymur-qmp-gen5x4-pcie-phy
- qcom,qcs615-qmp-gen3x1-pcie-phy
- qcom,qcs8300-qmp-gen4x2-pcie-phy
Expand Down Expand Up @@ -56,7 +57,7 @@ properties:

clocks:
minItems: 5
maxItems: 7
maxItems: 6

clock-names:
minItems: 5
Expand All @@ -67,7 +68,6 @@ properties:
- enum: [rchng, refgen]
- const: pipe
- const: pipediv2
- const: phy_aux

power-domains:
maxItems: 1
Expand Down Expand Up @@ -147,6 +147,7 @@ allOf:
compatible:
contains:
enum:
- qcom,kaanapali-qmp-gen3x2-pcie-phy
- qcom,qcs615-qmp-gen3x1-pcie-phy
- qcom,sar2130p-qmp-gen3x2-pcie-phy
- qcom,sc8180x-qmp-pcie-phy
Expand Down Expand Up @@ -179,7 +180,10 @@ allOf:
compatible:
contains:
enum:
- qcom,glymur-qmp-gen4x2-pcie-phy
- qcom,glymur-qmp-gen5x4-pcie-phy
- qcom,kaanapali-qmp-gen3x2-pcie-phy
- qcom,qcs8300-qmp-gen4x2-pcie-phy
- qcom,sa8775p-qmp-gen4x2-pcie-phy
- qcom,sa8775p-qmp-gen4x4-pcie-phy
- qcom,sc8280xp-qmp-gen3x1-pcie-phy
Expand Down Expand Up @@ -215,7 +219,9 @@ allOf:
compatible:
contains:
enum:
- qcom,glymur-qmp-gen4x2-pcie-phy
- qcom,glymur-qmp-gen5x4-pcie-phy
- qcom,kaanapali-qmp-gen3x2-pcie-phy
- qcom,sm8550-qmp-gen4x2-pcie-phy
- qcom,sm8650-qmp-gen4x2-pcie-phy
- qcom,x1e80100-qmp-gen3x2-pcie-phy
Expand Down
124 changes: 105 additions & 19 deletions drivers/pci/controller/dwc/pcie-qcom.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include <linux/of_pci.h>
#include <linux/pci.h>
#include <linux/pci-ecam.h>
#include <linux/pci-pwrctrl.h>
#include <linux/pm_opp.h>
#include <linux/pm_runtime.h>
#include <linux/platform_device.h>
Expand Down Expand Up @@ -267,10 +268,15 @@ struct qcom_pcie_cfg {
bool no_l0s;
};

struct qcom_pcie_perst {
struct list_head list;
struct gpio_desc *desc;
};

struct qcom_pcie_port {
struct list_head list;
struct gpio_desc *reset;
struct phy *phy;
struct list_head perst;
};

struct qcom_pcie {
Expand All @@ -291,11 +297,14 @@ struct qcom_pcie {

static void qcom_perst_assert(struct qcom_pcie *pcie, bool assert)
{
struct qcom_pcie_perst *perst;
struct qcom_pcie_port *port;
int val = assert ? 1 : 0;

list_for_each_entry(port, &pcie->ports, list)
gpiod_set_value_cansleep(port->reset, val);
list_for_each_entry(port, &pcie->ports, list) {
list_for_each_entry(perst, &port->perst, list)
gpiod_set_value_cansleep(perst->desc, val);
}

usleep_range(PERST_DELAY_US, PERST_DELAY_US + 500);
}
Expand Down Expand Up @@ -1310,10 +1319,18 @@ static int qcom_pcie_host_init(struct dw_pcie_rp *pp)
if (ret)
goto err_deinit;

ret = pci_pwrctrl_create_devices(pci->dev);
if (ret)
goto err_disable_phy;

ret = pci_pwrctrl_power_on_devices(pci->dev);
if (ret)
goto err_pwrctrl_destroy;

if (pcie->cfg->ops->post_init) {
ret = pcie->cfg->ops->post_init(pcie);
if (ret)
goto err_disable_phy;
goto err_pwrctrl_power_off;
}

qcom_ep_reset_deassert(pcie);
Expand All @@ -1328,6 +1345,10 @@ static int qcom_pcie_host_init(struct dw_pcie_rp *pp)

err_assert_reset:
qcom_ep_reset_assert(pcie);
err_pwrctrl_power_off:
pci_pwrctrl_power_off_devices(pci->dev);
err_pwrctrl_destroy:
pci_pwrctrl_destroy_devices(pci->dev);
err_disable_phy:
qcom_pcie_phy_power_off(pcie);
err_deinit:
Expand All @@ -1342,6 +1363,11 @@ static void qcom_pcie_host_deinit(struct dw_pcie_rp *pp)
struct qcom_pcie *pcie = to_qcom_pcie(pci);

qcom_ep_reset_assert(pcie);
/*
* No need to destroy pwrctrl devices as this function only gets called
* during system suspend as of now.
*/
pci_pwrctrl_power_off_devices(pci->dev);
qcom_pcie_phy_power_off(pcie);
pcie->cfg->ops->deinit(pcie);
}
Expand Down Expand Up @@ -1702,18 +1728,58 @@ static const struct pci_ecam_ops pci_qcom_ecam_ops = {
}
};

static int qcom_pcie_parse_port(struct qcom_pcie *pcie, struct device_node *node)
/* Parse PERST# from all nodes in depth first manner starting from @np */
static int qcom_pcie_parse_perst(struct qcom_pcie *pcie,
struct qcom_pcie_port *port,
struct device_node *np)
{
struct device *dev = pcie->pci->dev;
struct qcom_pcie_port *port;
struct qcom_pcie_perst *perst;
struct gpio_desc *reset;
struct phy *phy;
int ret;

reset = devm_fwnode_gpiod_get(dev, of_fwnode_handle(node),
"reset", GPIOD_OUT_HIGH, "PERST#");
if (IS_ERR(reset))
if (!of_find_property(np, "reset-gpios", NULL))
goto parse_child_node;

reset = devm_fwnode_gpiod_get(dev, of_fwnode_handle(np), "reset",
GPIOD_OUT_HIGH, "PERST#");
if (IS_ERR(reset)) {
/*
* FIXME: GPIOLIB currently supports exclusive GPIO access only.
* Non exclusive access is broken. But shared PERST# requires
* non-exclusive access. So once GPIOLIB properly supports it,
* implement it here.
*/
if (PTR_ERR(reset) == -EBUSY)
dev_err(dev, "Shared PERST# is not supported\n");

return PTR_ERR(reset);
}

perst = devm_kzalloc(dev, sizeof(*perst), GFP_KERNEL);
if (!perst)
return -ENOMEM;

INIT_LIST_HEAD(&perst->list);
perst->desc = reset;
list_add_tail(&perst->list, &port->perst);

parse_child_node:
for_each_available_child_of_node_scoped(np, child) {
ret = qcom_pcie_parse_perst(pcie, port, child);
if (ret)
return ret;
}

return 0;
}

static int qcom_pcie_parse_port(struct qcom_pcie *pcie, struct device_node *node)
{
struct device *dev = pcie->pci->dev;
struct qcom_pcie_port *port;
struct phy *phy;
int ret;

phy = devm_of_phy_get(dev, node, NULL);
if (IS_ERR(phy))
Expand All @@ -1727,7 +1793,12 @@ static int qcom_pcie_parse_port(struct qcom_pcie *pcie, struct device_node *node
if (ret)
return ret;

port->reset = reset;
INIT_LIST_HEAD(&port->perst);

ret = qcom_pcie_parse_perst(pcie, port, node);
if (ret)
return ret;

port->phy = phy;
INIT_LIST_HEAD(&port->list);
list_add_tail(&port->list, &pcie->ports);
Expand All @@ -1737,9 +1808,10 @@ static int qcom_pcie_parse_port(struct qcom_pcie *pcie, struct device_node *node

static int qcom_pcie_parse_ports(struct qcom_pcie *pcie)
{
struct qcom_pcie_perst *perst, *tmp_perst;
struct qcom_pcie_port *port, *tmp_port;
struct device *dev = pcie->pci->dev;
struct qcom_pcie_port *port, *tmp;
int ret = -ENOENT;
int ret = -ENODEV;

for_each_available_child_of_node_scoped(dev->of_node, of_port) {
if (!of_node_is_type(of_port, "pci"))
Expand All @@ -1752,7 +1824,9 @@ static int qcom_pcie_parse_ports(struct qcom_pcie *pcie)
return ret;

err_port_del:
list_for_each_entry_safe(port, tmp, &pcie->ports, list) {
list_for_each_entry_safe(port, tmp_port, &pcie->ports, list) {
list_for_each_entry_safe(perst, tmp_perst, &port->perst, list)
list_del(&perst->list);
phy_exit(port->phy);
list_del(&port->list);
}
Expand All @@ -1763,6 +1837,7 @@ static int qcom_pcie_parse_ports(struct qcom_pcie *pcie)
static int qcom_pcie_parse_legacy_binding(struct qcom_pcie *pcie)
{
struct device *dev = pcie->pci->dev;
struct qcom_pcie_perst *perst;
struct qcom_pcie_port *port;
struct gpio_desc *reset;
struct phy *phy;
Expand All @@ -1784,19 +1859,28 @@ static int qcom_pcie_parse_legacy_binding(struct qcom_pcie *pcie)
if (!port)
return -ENOMEM;

port->reset = reset;
perst = devm_kzalloc(dev, sizeof(*perst), GFP_KERNEL);
if (!perst)
return -ENOMEM;

port->phy = phy;
INIT_LIST_HEAD(&port->list);
list_add_tail(&port->list, &pcie->ports);

perst->desc = reset;
INIT_LIST_HEAD(&port->perst);
INIT_LIST_HEAD(&perst->list);
list_add_tail(&perst->list, &port->perst);

return 0;
}

static int qcom_pcie_probe(struct platform_device *pdev)
{
struct qcom_pcie_perst *perst, *tmp_perst;
struct qcom_pcie_port *port, *tmp_port;
const struct qcom_pcie_cfg *pcie_cfg;
unsigned long max_freq = ULONG_MAX;
struct qcom_pcie_port *port, *tmp;
struct device *dev = &pdev->dev;
struct dev_pm_opp *opp;
struct qcom_pcie *pcie;
Expand Down Expand Up @@ -1937,7 +2021,7 @@ static int qcom_pcie_probe(struct platform_device *pdev)

ret = qcom_pcie_parse_ports(pcie);
if (ret) {
if (ret != -ENOENT) {
if (ret != -ENODEV) {
dev_err_probe(pci->dev, ret,
"Failed to parse Root Port: %d\n", ret);
goto err_pm_runtime_put;
Expand All @@ -1961,7 +2045,7 @@ static int qcom_pcie_probe(struct platform_device *pdev)

ret = dw_pcie_host_init(pp);
if (ret) {
dev_err(dev, "cannot initialize host\n");
dev_err_probe(dev, ret, "cannot initialize host\n");
goto err_phy_exit;
}

Expand Down Expand Up @@ -1996,7 +2080,9 @@ static int qcom_pcie_probe(struct platform_device *pdev)
err_host_deinit:
dw_pcie_host_deinit(pp);
err_phy_exit:
list_for_each_entry_safe(port, tmp, &pcie->ports, list) {
list_for_each_entry_safe(port, tmp_port, &pcie->ports, list) {
list_for_each_entry_safe(perst, tmp_perst, &port->perst, list)
list_del(&perst->list);
phy_exit(port->phy);
list_del(&port->list);
}
Expand Down
1 change: 1 addition & 0 deletions drivers/pci/of.c
Original file line number Diff line number Diff line change
Expand Up @@ -867,6 +867,7 @@ bool of_pci_supply_present(struct device_node *np)

return false;
}
EXPORT_SYMBOL_GPL(of_pci_supply_present);

#endif /* CONFIG_PCI */

Expand Down
Loading