diff options
author | Bjorn Helgaas <bhelgaas@google.com> | 2016-12-12 12:25:10 -0500 |
---|---|---|
committer | Bjorn Helgaas <bhelgaas@google.com> | 2016-12-12 12:25:10 -0500 |
commit | aee10cd4c12aaa4d89d3355e669eb5025885094d (patch) | |
tree | 7f6d4263234ce9eac67c320e0310e10a0c9c1755 | |
parent | 46275d43c8dbf13d8b0c6e7a455608eeb220a677 (diff) | |
parent | d0491fc39bdd45575cd0094af18703d38665a309 (diff) |
Merge branch 'pci/host-qcom' into next
* pci/host-qcom:
PCI: qcom: Add support for MSM8996 PCIe controller
-rw-r--r-- | Documentation/devicetree/bindings/pci/qcom,pcie.txt | 14 | ||||
-rw-r--r-- | drivers/pci/host/pcie-qcom.c | 177 |
2 files changed, 185 insertions, 6 deletions
diff --git a/Documentation/devicetree/bindings/pci/qcom,pcie.txt b/Documentation/devicetree/bindings/pci/qcom,pcie.txt index 4059a6f89bc1..e15f9b19901f 100644 --- a/Documentation/devicetree/bindings/pci/qcom,pcie.txt +++ b/Documentation/devicetree/bindings/pci/qcom,pcie.txt | |||
@@ -7,6 +7,7 @@ | |||
7 | - "qcom,pcie-ipq8064" for ipq8064 | 7 | - "qcom,pcie-ipq8064" for ipq8064 |
8 | - "qcom,pcie-apq8064" for apq8064 | 8 | - "qcom,pcie-apq8064" for apq8064 |
9 | - "qcom,pcie-apq8084" for apq8084 | 9 | - "qcom,pcie-apq8084" for apq8084 |
10 | - "qcom,pcie-msm8996" for msm8996 or apq8096 | ||
10 | 11 | ||
11 | - reg: | 12 | - reg: |
12 | Usage: required | 13 | Usage: required |
@@ -92,6 +93,17 @@ | |||
92 | - "aux" Auxiliary (AUX) clock | 93 | - "aux" Auxiliary (AUX) clock |
93 | - "bus_master" Master AXI clock | 94 | - "bus_master" Master AXI clock |
94 | - "bus_slave" Slave AXI clock | 95 | - "bus_slave" Slave AXI clock |
96 | |||
97 | - clock-names: | ||
98 | Usage: required for msm8996/apq8096 | ||
99 | Value type: <stringlist> | ||
100 | Definition: Should contain the following entries | ||
101 | - "pipe" Pipe Clock driving internal logic | ||
102 | - "aux" Auxiliary (AUX) clock | ||
103 | - "cfg" Configuration clock | ||
104 | - "bus_master" Master AXI clock | ||
105 | - "bus_slave" Slave AXI clock | ||
106 | |||
95 | - resets: | 107 | - resets: |
96 | Usage: required | 108 | Usage: required |
97 | Value type: <prop-encoded-array> | 109 | Value type: <prop-encoded-array> |
@@ -115,7 +127,7 @@ | |||
115 | - "core" Core reset | 127 | - "core" Core reset |
116 | 128 | ||
117 | - power-domains: | 129 | - power-domains: |
118 | Usage: required for apq8084 | 130 | Usage: required for apq8084 and msm8996/apq8096 |
119 | Value type: <prop-encoded-array> | 131 | Value type: <prop-encoded-array> |
120 | Definition: A phandle and power domain specifier pair to the | 132 | Definition: A phandle and power domain specifier pair to the |
121 | power domain which is responsible for collapsing | 133 | power domain which is responsible for collapsing |
diff --git a/drivers/pci/host/pcie-qcom.c b/drivers/pci/host/pcie-qcom.c index ef0a84c7a588..8b02db2da6cc 100644 --- a/drivers/pci/host/pcie-qcom.c +++ b/drivers/pci/host/pcie-qcom.c | |||
@@ -36,11 +36,17 @@ | |||
36 | 36 | ||
37 | #include "pcie-designware.h" | 37 | #include "pcie-designware.h" |
38 | 38 | ||
39 | #define PCIE20_PARF_SYS_CTRL 0x00 | ||
39 | #define PCIE20_PARF_PHY_CTRL 0x40 | 40 | #define PCIE20_PARF_PHY_CTRL 0x40 |
40 | #define PCIE20_PARF_PHY_REFCLK 0x4C | 41 | #define PCIE20_PARF_PHY_REFCLK 0x4C |
41 | #define PCIE20_PARF_DBI_BASE_ADDR 0x168 | 42 | #define PCIE20_PARF_DBI_BASE_ADDR 0x168 |
42 | #define PCIE20_PARF_SLV_ADDR_SPACE_SIZE 0x16c | 43 | #define PCIE20_PARF_SLV_ADDR_SPACE_SIZE 0x16C |
44 | #define PCIE20_PARF_MHI_CLOCK_RESET_CTRL 0x174 | ||
43 | #define PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT 0x178 | 45 | #define PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT 0x178 |
46 | #define PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT_V2 0x1A8 | ||
47 | #define PCIE20_PARF_LTSSM 0x1B0 | ||
48 | #define PCIE20_PARF_SID_OFFSET 0x234 | ||
49 | #define PCIE20_PARF_BDF_TRANSLATE_CFG 0x24C | ||
44 | 50 | ||
45 | #define PCIE20_ELBI_SYS_CTRL 0x04 | 51 | #define PCIE20_ELBI_SYS_CTRL 0x04 |
46 | #define PCIE20_ELBI_SYS_CTRL_LT_ENABLE BIT(0) | 52 | #define PCIE20_ELBI_SYS_CTRL_LT_ENABLE BIT(0) |
@@ -72,9 +78,18 @@ struct qcom_pcie_resources_v1 { | |||
72 | struct regulator *vdda; | 78 | struct regulator *vdda; |
73 | }; | 79 | }; |
74 | 80 | ||
81 | struct qcom_pcie_resources_v2 { | ||
82 | struct clk *aux_clk; | ||
83 | struct clk *master_clk; | ||
84 | struct clk *slave_clk; | ||
85 | struct clk *cfg_clk; | ||
86 | struct clk *pipe_clk; | ||
87 | }; | ||
88 | |||
75 | union qcom_pcie_resources { | 89 | union qcom_pcie_resources { |
76 | struct qcom_pcie_resources_v0 v0; | 90 | struct qcom_pcie_resources_v0 v0; |
77 | struct qcom_pcie_resources_v1 v1; | 91 | struct qcom_pcie_resources_v1 v1; |
92 | struct qcom_pcie_resources_v2 v2; | ||
78 | }; | 93 | }; |
79 | 94 | ||
80 | struct qcom_pcie; | 95 | struct qcom_pcie; |
@@ -82,7 +97,9 @@ struct qcom_pcie; | |||
82 | struct qcom_pcie_ops { | 97 | struct qcom_pcie_ops { |
83 | int (*get_resources)(struct qcom_pcie *pcie); | 98 | int (*get_resources)(struct qcom_pcie *pcie); |
84 | int (*init)(struct qcom_pcie *pcie); | 99 | int (*init)(struct qcom_pcie *pcie); |
100 | int (*post_init)(struct qcom_pcie *pcie); | ||
85 | void (*deinit)(struct qcom_pcie *pcie); | 101 | void (*deinit)(struct qcom_pcie *pcie); |
102 | void (*ltssm_enable)(struct qcom_pcie *pcie); | ||
86 | }; | 103 | }; |
87 | 104 | ||
88 | struct qcom_pcie { | 105 | struct qcom_pcie { |
@@ -116,17 +133,35 @@ static irqreturn_t qcom_pcie_msi_irq_handler(int irq, void *arg) | |||
116 | return dw_handle_msi_irq(pp); | 133 | return dw_handle_msi_irq(pp); |
117 | } | 134 | } |
118 | 135 | ||
119 | static int qcom_pcie_establish_link(struct qcom_pcie *pcie) | 136 | static void qcom_pcie_v0_v1_ltssm_enable(struct qcom_pcie *pcie) |
120 | { | 137 | { |
121 | u32 val; | 138 | u32 val; |
122 | 139 | ||
123 | if (dw_pcie_link_up(&pcie->pp)) | ||
124 | return 0; | ||
125 | |||
126 | /* enable link training */ | 140 | /* enable link training */ |
127 | val = readl(pcie->elbi + PCIE20_ELBI_SYS_CTRL); | 141 | val = readl(pcie->elbi + PCIE20_ELBI_SYS_CTRL); |
128 | val |= PCIE20_ELBI_SYS_CTRL_LT_ENABLE; | 142 | val |= PCIE20_ELBI_SYS_CTRL_LT_ENABLE; |
129 | writel(val, pcie->elbi + PCIE20_ELBI_SYS_CTRL); | 143 | writel(val, pcie->elbi + PCIE20_ELBI_SYS_CTRL); |
144 | } | ||
145 | |||
146 | static void qcom_pcie_v2_ltssm_enable(struct qcom_pcie *pcie) | ||
147 | { | ||
148 | u32 val; | ||
149 | |||
150 | /* enable link training */ | ||
151 | val = readl(pcie->parf + PCIE20_PARF_LTSSM); | ||
152 | val |= BIT(8); | ||
153 | writel(val, pcie->parf + PCIE20_PARF_LTSSM); | ||
154 | } | ||
155 | |||
156 | static int qcom_pcie_establish_link(struct qcom_pcie *pcie) | ||
157 | { | ||
158 | |||
159 | if (dw_pcie_link_up(&pcie->pp)) | ||
160 | return 0; | ||
161 | |||
162 | /* Enable Link Training state machine */ | ||
163 | if (pcie->ops->ltssm_enable) | ||
164 | pcie->ops->ltssm_enable(pcie); | ||
130 | 165 | ||
131 | return dw_pcie_wait_for_link(&pcie->pp); | 166 | return dw_pcie_wait_for_link(&pcie->pp); |
132 | } | 167 | } |
@@ -421,6 +456,113 @@ err_res: | |||
421 | return ret; | 456 | return ret; |
422 | } | 457 | } |
423 | 458 | ||
459 | static int qcom_pcie_get_resources_v2(struct qcom_pcie *pcie) | ||
460 | { | ||
461 | struct qcom_pcie_resources_v2 *res = &pcie->res.v2; | ||
462 | struct device *dev = pcie->pp.dev; | ||
463 | |||
464 | res->aux_clk = devm_clk_get(dev, "aux"); | ||
465 | if (IS_ERR(res->aux_clk)) | ||
466 | return PTR_ERR(res->aux_clk); | ||
467 | |||
468 | res->cfg_clk = devm_clk_get(dev, "cfg"); | ||
469 | if (IS_ERR(res->cfg_clk)) | ||
470 | return PTR_ERR(res->cfg_clk); | ||
471 | |||
472 | res->master_clk = devm_clk_get(dev, "bus_master"); | ||
473 | if (IS_ERR(res->master_clk)) | ||
474 | return PTR_ERR(res->master_clk); | ||
475 | |||
476 | res->slave_clk = devm_clk_get(dev, "bus_slave"); | ||
477 | if (IS_ERR(res->slave_clk)) | ||
478 | return PTR_ERR(res->slave_clk); | ||
479 | |||
480 | res->pipe_clk = devm_clk_get(dev, "pipe"); | ||
481 | if (IS_ERR(res->pipe_clk)) | ||
482 | return PTR_ERR(res->pipe_clk); | ||
483 | |||
484 | return 0; | ||
485 | } | ||
486 | |||
487 | static int qcom_pcie_init_v2(struct qcom_pcie *pcie) | ||
488 | { | ||
489 | struct qcom_pcie_resources_v2 *res = &pcie->res.v2; | ||
490 | struct device *dev = pcie->pp.dev; | ||
491 | u32 val; | ||
492 | int ret; | ||
493 | |||
494 | ret = clk_prepare_enable(res->aux_clk); | ||
495 | if (ret) { | ||
496 | dev_err(dev, "cannot prepare/enable aux clock\n"); | ||
497 | return ret; | ||
498 | } | ||
499 | |||
500 | ret = clk_prepare_enable(res->cfg_clk); | ||
501 | if (ret) { | ||
502 | dev_err(dev, "cannot prepare/enable cfg clock\n"); | ||
503 | goto err_cfg_clk; | ||
504 | } | ||
505 | |||
506 | ret = clk_prepare_enable(res->master_clk); | ||
507 | if (ret) { | ||
508 | dev_err(dev, "cannot prepare/enable master clock\n"); | ||
509 | goto err_master_clk; | ||
510 | } | ||
511 | |||
512 | ret = clk_prepare_enable(res->slave_clk); | ||
513 | if (ret) { | ||
514 | dev_err(dev, "cannot prepare/enable slave clock\n"); | ||
515 | goto err_slave_clk; | ||
516 | } | ||
517 | |||
518 | /* enable PCIe clocks and resets */ | ||
519 | val = readl(pcie->parf + PCIE20_PARF_PHY_CTRL); | ||
520 | val &= ~BIT(0); | ||
521 | writel(val, pcie->parf + PCIE20_PARF_PHY_CTRL); | ||
522 | |||
523 | /* change DBI base address */ | ||
524 | writel(0, pcie->parf + PCIE20_PARF_DBI_BASE_ADDR); | ||
525 | |||
526 | /* MAC PHY_POWERDOWN MUX DISABLE */ | ||
527 | val = readl(pcie->parf + PCIE20_PARF_SYS_CTRL); | ||
528 | val &= ~BIT(29); | ||
529 | writel(val, pcie->parf + PCIE20_PARF_SYS_CTRL); | ||
530 | |||
531 | val = readl(pcie->parf + PCIE20_PARF_MHI_CLOCK_RESET_CTRL); | ||
532 | val |= BIT(4); | ||
533 | writel(val, pcie->parf + PCIE20_PARF_MHI_CLOCK_RESET_CTRL); | ||
534 | |||
535 | val = readl(pcie->parf + PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT_V2); | ||
536 | val |= BIT(31); | ||
537 | writel(val, pcie->parf + PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT_V2); | ||
538 | |||
539 | return 0; | ||
540 | |||
541 | err_slave_clk: | ||
542 | clk_disable_unprepare(res->master_clk); | ||
543 | err_master_clk: | ||
544 | clk_disable_unprepare(res->cfg_clk); | ||
545 | err_cfg_clk: | ||
546 | clk_disable_unprepare(res->aux_clk); | ||
547 | |||
548 | return ret; | ||
549 | } | ||
550 | |||
551 | static int qcom_pcie_post_init_v2(struct qcom_pcie *pcie) | ||
552 | { | ||
553 | struct qcom_pcie_resources_v2 *res = &pcie->res.v2; | ||
554 | struct device *dev = pcie->pp.dev; | ||
555 | int ret; | ||
556 | |||
557 | ret = clk_prepare_enable(res->pipe_clk); | ||
558 | if (ret) { | ||
559 | dev_err(dev, "cannot prepare/enable pipe clock\n"); | ||
560 | return ret; | ||
561 | } | ||
562 | |||
563 | return 0; | ||
564 | } | ||
565 | |||
424 | static int qcom_pcie_link_up(struct pcie_port *pp) | 566 | static int qcom_pcie_link_up(struct pcie_port *pp) |
425 | { | 567 | { |
426 | struct qcom_pcie *pcie = to_qcom_pcie(pp); | 568 | struct qcom_pcie *pcie = to_qcom_pcie(pp); |
@@ -429,6 +571,17 @@ static int qcom_pcie_link_up(struct pcie_port *pp) | |||
429 | return !!(val & PCI_EXP_LNKSTA_DLLLA); | 571 | return !!(val & PCI_EXP_LNKSTA_DLLLA); |
430 | } | 572 | } |
431 | 573 | ||
574 | static void qcom_pcie_deinit_v2(struct qcom_pcie *pcie) | ||
575 | { | ||
576 | struct qcom_pcie_resources_v2 *res = &pcie->res.v2; | ||
577 | |||
578 | clk_disable_unprepare(res->pipe_clk); | ||
579 | clk_disable_unprepare(res->slave_clk); | ||
580 | clk_disable_unprepare(res->master_clk); | ||
581 | clk_disable_unprepare(res->cfg_clk); | ||
582 | clk_disable_unprepare(res->aux_clk); | ||
583 | } | ||
584 | |||
432 | static void qcom_pcie_host_init(struct pcie_port *pp) | 585 | static void qcom_pcie_host_init(struct pcie_port *pp) |
433 | { | 586 | { |
434 | struct qcom_pcie *pcie = to_qcom_pcie(pp); | 587 | struct qcom_pcie *pcie = to_qcom_pcie(pp); |
@@ -444,6 +597,9 @@ static void qcom_pcie_host_init(struct pcie_port *pp) | |||
444 | if (ret) | 597 | if (ret) |
445 | goto err_deinit; | 598 | goto err_deinit; |
446 | 599 | ||
600 | if (pcie->ops->post_init) | ||
601 | pcie->ops->post_init(pcie); | ||
602 | |||
447 | dw_pcie_setup_rc(pp); | 603 | dw_pcie_setup_rc(pp); |
448 | 604 | ||
449 | if (IS_ENABLED(CONFIG_PCI_MSI)) | 605 | if (IS_ENABLED(CONFIG_PCI_MSI)) |
@@ -487,12 +643,22 @@ static const struct qcom_pcie_ops ops_v0 = { | |||
487 | .get_resources = qcom_pcie_get_resources_v0, | 643 | .get_resources = qcom_pcie_get_resources_v0, |
488 | .init = qcom_pcie_init_v0, | 644 | .init = qcom_pcie_init_v0, |
489 | .deinit = qcom_pcie_deinit_v0, | 645 | .deinit = qcom_pcie_deinit_v0, |
646 | .ltssm_enable = qcom_pcie_v0_v1_ltssm_enable, | ||
490 | }; | 647 | }; |
491 | 648 | ||
492 | static const struct qcom_pcie_ops ops_v1 = { | 649 | static const struct qcom_pcie_ops ops_v1 = { |
493 | .get_resources = qcom_pcie_get_resources_v1, | 650 | .get_resources = qcom_pcie_get_resources_v1, |
494 | .init = qcom_pcie_init_v1, | 651 | .init = qcom_pcie_init_v1, |
495 | .deinit = qcom_pcie_deinit_v1, | 652 | .deinit = qcom_pcie_deinit_v1, |
653 | .ltssm_enable = qcom_pcie_v0_v1_ltssm_enable, | ||
654 | }; | ||
655 | |||
656 | static const struct qcom_pcie_ops ops_v2 = { | ||
657 | .get_resources = qcom_pcie_get_resources_v2, | ||
658 | .init = qcom_pcie_init_v2, | ||
659 | .post_init = qcom_pcie_post_init_v2, | ||
660 | .deinit = qcom_pcie_deinit_v2, | ||
661 | .ltssm_enable = qcom_pcie_v2_ltssm_enable, | ||
496 | }; | 662 | }; |
497 | 663 | ||
498 | static int qcom_pcie_probe(struct platform_device *pdev) | 664 | static int qcom_pcie_probe(struct platform_device *pdev) |
@@ -572,6 +738,7 @@ static const struct of_device_id qcom_pcie_match[] = { | |||
572 | { .compatible = "qcom,pcie-ipq8064", .data = &ops_v0 }, | 738 | { .compatible = "qcom,pcie-ipq8064", .data = &ops_v0 }, |
573 | { .compatible = "qcom,pcie-apq8064", .data = &ops_v0 }, | 739 | { .compatible = "qcom,pcie-apq8064", .data = &ops_v0 }, |
574 | { .compatible = "qcom,pcie-apq8084", .data = &ops_v1 }, | 740 | { .compatible = "qcom,pcie-apq8084", .data = &ops_v1 }, |
741 | { .compatible = "qcom,pcie-msm8996", .data = &ops_v2 }, | ||
575 | { } | 742 | { } |
576 | }; | 743 | }; |
577 | 744 | ||