diff options
author | Bjorn Helgaas <bhelgaas@google.com> | 2015-11-03 09:39:32 -0500 |
---|---|---|
committer | Bjorn Helgaas <bhelgaas@google.com> | 2015-11-03 09:39:32 -0500 |
commit | 7225107e158241b7e22f15b9f4c3db0b3330984d (patch) | |
tree | 7050af734ec625ac7ee53b8487b8ece65a5f0967 | |
parent | 4ed31f24a6416cc60bc8e92c25b2de3060fbedf8 (diff) | |
parent | bd33b87a9a15f8182e8c6a49f5413e7ef79372bf (diff) |
Merge branch 'pci/host-layerscape' into next
* pci/host-layerscape:
PCI: layerscape: Add ls_pcie_msi_host_init()
PCI: layerscape: Add support for LS1043a and LS2080a
PCI: layerscape: Remove unused fields from struct ls_pcie
PCI: layerscape: Update ls_add_pcie_port()
PCI: layerscape: Factor out SCFG related function
PCI: layerscape: Ignore PCIe controllers in Endpoint mode
PCI: layerscape: Remove ls_pcie_establish_link()
-rw-r--r-- | Documentation/devicetree/bindings/pci/layerscape-pci.txt | 14 | ||||
-rw-r--r-- | drivers/pci/host/Kconfig | 2 | ||||
-rw-r--r-- | drivers/pci/host/pci-layerscape.c | 199 |
3 files changed, 161 insertions, 54 deletions
diff --git a/Documentation/devicetree/bindings/pci/layerscape-pci.txt b/Documentation/devicetree/bindings/pci/layerscape-pci.txt index 6286f049bf18..e3767857d30d 100644 --- a/Documentation/devicetree/bindings/pci/layerscape-pci.txt +++ b/Documentation/devicetree/bindings/pci/layerscape-pci.txt | |||
@@ -1,10 +1,20 @@ | |||
1 | Freescale Layerscape PCIe controller | 1 | Freescale Layerscape PCIe controller |
2 | 2 | ||
3 | This PCIe host controller is based on the Synopsis Designware PCIe IP | 3 | This PCIe host controller is based on the Synopsys DesignWare PCIe IP |
4 | and thus inherits all the common properties defined in designware-pcie.txt. | 4 | and thus inherits all the common properties defined in designware-pcie.txt. |
5 | 5 | ||
6 | This controller derives its clocks from the Reset Configuration Word (RCW) | ||
7 | which is used to describe the PLL settings at the time of chip-reset. | ||
8 | |||
9 | Also as per the available Reference Manuals, there is no specific 'version' | ||
10 | register available in the Freescale PCIe controller register set, | ||
11 | which can allow determining the underlying DesignWare PCIe controller version | ||
12 | information. | ||
13 | |||
6 | Required properties: | 14 | Required properties: |
7 | - compatible: should contain the platform identifier such as "fsl,ls1021a-pcie" | 15 | - compatible: should contain the platform identifier such as: |
16 | "fsl,ls1021a-pcie", "snps,dw-pcie" | ||
17 | "fsl,ls2080a-pcie", "snps,dw-pcie" | ||
8 | - reg: base addresses and lengths of the PCIe controller | 18 | - reg: base addresses and lengths of the PCIe controller |
9 | - interrupts: A list of interrupt outputs of the controller. Must contain an | 19 | - interrupts: A list of interrupt outputs of the controller. Must contain an |
10 | entry for each entry in the interrupt-names property. | 20 | entry for each entry in the interrupt-names property. |
diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig index 70370f28da87..f131ba947dc6 100644 --- a/drivers/pci/host/Kconfig +++ b/drivers/pci/host/Kconfig | |||
@@ -107,7 +107,7 @@ config PCI_XGENE_MSI | |||
107 | 107 | ||
108 | config PCI_LAYERSCAPE | 108 | config PCI_LAYERSCAPE |
109 | bool "Freescale Layerscape PCIe controller" | 109 | bool "Freescale Layerscape PCIe controller" |
110 | depends on OF && ARM | 110 | depends on OF && (ARM || ARCH_LAYERSCAPE) |
111 | select PCIE_DW | 111 | select PCIE_DW |
112 | select MFD_SYSCON | 112 | select MFD_SYSCON |
113 | help | 113 | help |
diff --git a/drivers/pci/host/pci-layerscape.c b/drivers/pci/host/pci-layerscape.c index b2328ea13dcf..3923bed93c7e 100644 --- a/drivers/pci/host/pci-layerscape.c +++ b/drivers/pci/host/pci-layerscape.c | |||
@@ -3,7 +3,7 @@ | |||
3 | * | 3 | * |
4 | * Copyright (C) 2014 Freescale Semiconductor. | 4 | * Copyright (C) 2014 Freescale Semiconductor. |
5 | * | 5 | * |
6 | * Author: Minghuan Lian <Minghuan.Lian@freescale.com> | 6 | * Author: Minghuan Lian <Minghuan.Lian@freescale.com> |
7 | * | 7 | * |
8 | * This program is free software; you can redistribute it and/or modify | 8 | * This program is free software; you can redistribute it and/or modify |
9 | * it under the terms of the GNU General Public License version 2 as | 9 | * it under the terms of the GNU General Public License version 2 as |
@@ -11,7 +11,6 @@ | |||
11 | */ | 11 | */ |
12 | 12 | ||
13 | #include <linux/kernel.h> | 13 | #include <linux/kernel.h> |
14 | #include <linux/delay.h> | ||
15 | #include <linux/interrupt.h> | 14 | #include <linux/interrupt.h> |
16 | #include <linux/module.h> | 15 | #include <linux/module.h> |
17 | #include <linux/of_pci.h> | 16 | #include <linux/of_pci.h> |
@@ -32,27 +31,60 @@ | |||
32 | #define LTSSM_STATE_MASK 0x3f | 31 | #define LTSSM_STATE_MASK 0x3f |
33 | #define LTSSM_PCIE_L0 0x11 /* L0 state */ | 32 | #define LTSSM_PCIE_L0 0x11 /* L0 state */ |
34 | 33 | ||
35 | /* Symbol Timer Register and Filter Mask Register 1 */ | 34 | /* PEX Internal Configuration Registers */ |
36 | #define PCIE_STRFMR1 0x71c | 35 | #define PCIE_STRFMR1 0x71c /* Symbol Timer & Filter Mask Register1 */ |
36 | #define PCIE_DBI_RO_WR_EN 0x8bc /* DBI Read-Only Write Enable Register */ | ||
37 | |||
38 | /* PEX LUT registers */ | ||
39 | #define PCIE_LUT_DBG 0x7FC /* PEX LUT Debug Register */ | ||
40 | |||
41 | struct ls_pcie_drvdata { | ||
42 | u32 lut_offset; | ||
43 | u32 ltssm_shift; | ||
44 | struct pcie_host_ops *ops; | ||
45 | }; | ||
37 | 46 | ||
38 | struct ls_pcie { | 47 | struct ls_pcie { |
39 | struct list_head node; | ||
40 | struct device *dev; | ||
41 | struct pci_bus *bus; | ||
42 | void __iomem *dbi; | 48 | void __iomem *dbi; |
49 | void __iomem *lut; | ||
43 | struct regmap *scfg; | 50 | struct regmap *scfg; |
44 | struct pcie_port pp; | 51 | struct pcie_port pp; |
52 | const struct ls_pcie_drvdata *drvdata; | ||
45 | int index; | 53 | int index; |
46 | int msi_irq; | ||
47 | }; | 54 | }; |
48 | 55 | ||
49 | #define to_ls_pcie(x) container_of(x, struct ls_pcie, pp) | 56 | #define to_ls_pcie(x) container_of(x, struct ls_pcie, pp) |
50 | 57 | ||
51 | static int ls_pcie_link_up(struct pcie_port *pp) | 58 | static bool ls_pcie_is_bridge(struct ls_pcie *pcie) |
59 | { | ||
60 | u32 header_type; | ||
61 | |||
62 | header_type = ioread8(pcie->dbi + PCI_HEADER_TYPE); | ||
63 | header_type &= 0x7f; | ||
64 | |||
65 | return header_type == PCI_HEADER_TYPE_BRIDGE; | ||
66 | } | ||
67 | |||
68 | /* Clear multi-function bit */ | ||
69 | static void ls_pcie_clear_multifunction(struct ls_pcie *pcie) | ||
70 | { | ||
71 | iowrite8(PCI_HEADER_TYPE_BRIDGE, pcie->dbi + PCI_HEADER_TYPE); | ||
72 | } | ||
73 | |||
74 | /* Fix class value */ | ||
75 | static void ls_pcie_fix_class(struct ls_pcie *pcie) | ||
76 | { | ||
77 | iowrite16(PCI_CLASS_BRIDGE_PCI, pcie->dbi + PCI_CLASS_DEVICE); | ||
78 | } | ||
79 | |||
80 | static int ls1021_pcie_link_up(struct pcie_port *pp) | ||
52 | { | 81 | { |
53 | u32 state; | 82 | u32 state; |
54 | struct ls_pcie *pcie = to_ls_pcie(pp); | 83 | struct ls_pcie *pcie = to_ls_pcie(pp); |
55 | 84 | ||
85 | if (!pcie->scfg) | ||
86 | return 0; | ||
87 | |||
56 | regmap_read(pcie->scfg, SCFG_PEXMSCPORTSR(pcie->index), &state); | 88 | regmap_read(pcie->scfg, SCFG_PEXMSCPORTSR(pcie->index), &state); |
57 | state = (state >> LTSSM_STATE_SHIFT) & LTSSM_STATE_MASK; | 89 | state = (state >> LTSSM_STATE_SHIFT) & LTSSM_STATE_MASK; |
58 | 90 | ||
@@ -62,27 +94,27 @@ static int ls_pcie_link_up(struct pcie_port *pp) | |||
62 | return 1; | 94 | return 1; |
63 | } | 95 | } |
64 | 96 | ||
65 | static int ls_pcie_establish_link(struct pcie_port *pp) | 97 | static void ls1021_pcie_host_init(struct pcie_port *pp) |
66 | { | 98 | { |
67 | unsigned int retries; | 99 | struct ls_pcie *pcie = to_ls_pcie(pp); |
100 | u32 val, index[2]; | ||
68 | 101 | ||
69 | for (retries = 0; retries < 200; retries++) { | 102 | pcie->scfg = syscon_regmap_lookup_by_phandle(pp->dev->of_node, |
70 | if (dw_pcie_link_up(pp)) | 103 | "fsl,pcie-scfg"); |
71 | return 0; | 104 | if (IS_ERR(pcie->scfg)) { |
72 | usleep_range(100, 1000); | 105 | dev_err(pp->dev, "No syscfg phandle specified\n"); |
106 | pcie->scfg = NULL; | ||
107 | return; | ||
73 | } | 108 | } |
74 | 109 | ||
75 | dev_err(pp->dev, "phy link never came up\n"); | 110 | if (of_property_read_u32_array(pp->dev->of_node, |
76 | return -EINVAL; | 111 | "fsl,pcie-scfg", index, 2)) { |
77 | } | 112 | pcie->scfg = NULL; |
78 | 113 | return; | |
79 | static void ls_pcie_host_init(struct pcie_port *pp) | 114 | } |
80 | { | 115 | pcie->index = index[1]; |
81 | struct ls_pcie *pcie = to_ls_pcie(pp); | ||
82 | u32 val; | ||
83 | 116 | ||
84 | dw_pcie_setup_rc(pp); | 117 | dw_pcie_setup_rc(pp); |
85 | ls_pcie_establish_link(pp); | ||
86 | 118 | ||
87 | /* | 119 | /* |
88 | * LS1021A Workaround for internal TKT228622 | 120 | * LS1021A Workaround for internal TKT228622 |
@@ -93,21 +125,97 @@ static void ls_pcie_host_init(struct pcie_port *pp) | |||
93 | iowrite32(val, pcie->dbi + PCIE_STRFMR1); | 125 | iowrite32(val, pcie->dbi + PCIE_STRFMR1); |
94 | } | 126 | } |
95 | 127 | ||
128 | static int ls_pcie_link_up(struct pcie_port *pp) | ||
129 | { | ||
130 | struct ls_pcie *pcie = to_ls_pcie(pp); | ||
131 | u32 state; | ||
132 | |||
133 | state = (ioread32(pcie->lut + PCIE_LUT_DBG) >> | ||
134 | pcie->drvdata->ltssm_shift) & | ||
135 | LTSSM_STATE_MASK; | ||
136 | |||
137 | if (state < LTSSM_PCIE_L0) | ||
138 | return 0; | ||
139 | |||
140 | return 1; | ||
141 | } | ||
142 | |||
143 | static void ls_pcie_host_init(struct pcie_port *pp) | ||
144 | { | ||
145 | struct ls_pcie *pcie = to_ls_pcie(pp); | ||
146 | |||
147 | iowrite32(1, pcie->dbi + PCIE_DBI_RO_WR_EN); | ||
148 | ls_pcie_fix_class(pcie); | ||
149 | ls_pcie_clear_multifunction(pcie); | ||
150 | iowrite32(0, pcie->dbi + PCIE_DBI_RO_WR_EN); | ||
151 | } | ||
152 | |||
153 | static int ls_pcie_msi_host_init(struct pcie_port *pp, | ||
154 | struct msi_controller *chip) | ||
155 | { | ||
156 | struct device_node *msi_node; | ||
157 | struct device_node *np = pp->dev->of_node; | ||
158 | |||
159 | /* | ||
160 | * The MSI domain is set by the generic of_msi_configure(). This | ||
161 | * .msi_host_init() function keeps us from doing the default MSI | ||
162 | * domain setup in dw_pcie_host_init() and also enforces the | ||
163 | * requirement that "msi-parent" exists. | ||
164 | */ | ||
165 | msi_node = of_parse_phandle(np, "msi-parent", 0); | ||
166 | if (!msi_node) { | ||
167 | dev_err(pp->dev, "failed to find msi-parent\n"); | ||
168 | return -EINVAL; | ||
169 | } | ||
170 | |||
171 | return 0; | ||
172 | } | ||
173 | |||
174 | static struct pcie_host_ops ls1021_pcie_host_ops = { | ||
175 | .link_up = ls1021_pcie_link_up, | ||
176 | .host_init = ls1021_pcie_host_init, | ||
177 | .msi_host_init = ls_pcie_msi_host_init, | ||
178 | }; | ||
179 | |||
96 | static struct pcie_host_ops ls_pcie_host_ops = { | 180 | static struct pcie_host_ops ls_pcie_host_ops = { |
97 | .link_up = ls_pcie_link_up, | 181 | .link_up = ls_pcie_link_up, |
98 | .host_init = ls_pcie_host_init, | 182 | .host_init = ls_pcie_host_init, |
183 | .msi_host_init = ls_pcie_msi_host_init, | ||
184 | }; | ||
185 | |||
186 | static struct ls_pcie_drvdata ls1021_drvdata = { | ||
187 | .ops = &ls1021_pcie_host_ops, | ||
188 | }; | ||
189 | |||
190 | static struct ls_pcie_drvdata ls1043_drvdata = { | ||
191 | .lut_offset = 0x10000, | ||
192 | .ltssm_shift = 24, | ||
193 | .ops = &ls_pcie_host_ops, | ||
99 | }; | 194 | }; |
100 | 195 | ||
101 | static int ls_add_pcie_port(struct ls_pcie *pcie) | 196 | static struct ls_pcie_drvdata ls2080_drvdata = { |
197 | .lut_offset = 0x80000, | ||
198 | .ltssm_shift = 0, | ||
199 | .ops = &ls_pcie_host_ops, | ||
200 | }; | ||
201 | |||
202 | static const struct of_device_id ls_pcie_of_match[] = { | ||
203 | { .compatible = "fsl,ls1021a-pcie", .data = &ls1021_drvdata }, | ||
204 | { .compatible = "fsl,ls1043a-pcie", .data = &ls1043_drvdata }, | ||
205 | { .compatible = "fsl,ls2080a-pcie", .data = &ls2080_drvdata }, | ||
206 | { }, | ||
207 | }; | ||
208 | MODULE_DEVICE_TABLE(of, ls_pcie_of_match); | ||
209 | |||
210 | static int __init ls_add_pcie_port(struct pcie_port *pp, | ||
211 | struct platform_device *pdev) | ||
102 | { | 212 | { |
103 | struct pcie_port *pp; | ||
104 | int ret; | 213 | int ret; |
214 | struct ls_pcie *pcie = to_ls_pcie(pp); | ||
105 | 215 | ||
106 | pp = &pcie->pp; | 216 | pp->dev = &pdev->dev; |
107 | pp->dev = pcie->dev; | ||
108 | pp->dbi_base = pcie->dbi; | 217 | pp->dbi_base = pcie->dbi; |
109 | pp->root_bus_nr = -1; | 218 | pp->ops = pcie->drvdata->ops; |
110 | pp->ops = &ls_pcie_host_ops; | ||
111 | 219 | ||
112 | ret = dw_pcie_host_init(pp); | 220 | ret = dw_pcie_host_init(pp); |
113 | if (ret) { | 221 | if (ret) { |
@@ -120,17 +228,19 @@ static int ls_add_pcie_port(struct ls_pcie *pcie) | |||
120 | 228 | ||
121 | static int __init ls_pcie_probe(struct platform_device *pdev) | 229 | static int __init ls_pcie_probe(struct platform_device *pdev) |
122 | { | 230 | { |
231 | const struct of_device_id *match; | ||
123 | struct ls_pcie *pcie; | 232 | struct ls_pcie *pcie; |
124 | struct resource *dbi_base; | 233 | struct resource *dbi_base; |
125 | u32 index[2]; | ||
126 | int ret; | 234 | int ret; |
127 | 235 | ||
236 | match = of_match_device(ls_pcie_of_match, &pdev->dev); | ||
237 | if (!match) | ||
238 | return -ENODEV; | ||
239 | |||
128 | pcie = devm_kzalloc(&pdev->dev, sizeof(*pcie), GFP_KERNEL); | 240 | pcie = devm_kzalloc(&pdev->dev, sizeof(*pcie), GFP_KERNEL); |
129 | if (!pcie) | 241 | if (!pcie) |
130 | return -ENOMEM; | 242 | return -ENOMEM; |
131 | 243 | ||
132 | pcie->dev = &pdev->dev; | ||
133 | |||
134 | dbi_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs"); | 244 | dbi_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs"); |
135 | pcie->dbi = devm_ioremap_resource(&pdev->dev, dbi_base); | 245 | pcie->dbi = devm_ioremap_resource(&pdev->dev, dbi_base); |
136 | if (IS_ERR(pcie->dbi)) { | 246 | if (IS_ERR(pcie->dbi)) { |
@@ -138,20 +248,13 @@ static int __init ls_pcie_probe(struct platform_device *pdev) | |||
138 | return PTR_ERR(pcie->dbi); | 248 | return PTR_ERR(pcie->dbi); |
139 | } | 249 | } |
140 | 250 | ||
141 | pcie->scfg = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, | 251 | pcie->drvdata = match->data; |
142 | "fsl,pcie-scfg"); | 252 | pcie->lut = pcie->dbi + pcie->drvdata->lut_offset; |
143 | if (IS_ERR(pcie->scfg)) { | ||
144 | dev_err(&pdev->dev, "No syscfg phandle specified\n"); | ||
145 | return PTR_ERR(pcie->scfg); | ||
146 | } | ||
147 | 253 | ||
148 | ret = of_property_read_u32_array(pdev->dev.of_node, | 254 | if (!ls_pcie_is_bridge(pcie)) |
149 | "fsl,pcie-scfg", index, 2); | 255 | return -ENODEV; |
150 | if (ret) | ||
151 | return ret; | ||
152 | pcie->index = index[1]; | ||
153 | 256 | ||
154 | ret = ls_add_pcie_port(pcie); | 257 | ret = ls_add_pcie_port(&pcie->pp, pdev); |
155 | if (ret < 0) | 258 | if (ret < 0) |
156 | return ret; | 259 | return ret; |
157 | 260 | ||
@@ -160,12 +263,6 @@ static int __init ls_pcie_probe(struct platform_device *pdev) | |||
160 | return 0; | 263 | return 0; |
161 | } | 264 | } |
162 | 265 | ||
163 | static const struct of_device_id ls_pcie_of_match[] = { | ||
164 | { .compatible = "fsl,ls1021a-pcie" }, | ||
165 | { }, | ||
166 | }; | ||
167 | MODULE_DEVICE_TABLE(of, ls_pcie_of_match); | ||
168 | |||
169 | static struct platform_driver ls_pcie_driver = { | 266 | static struct platform_driver ls_pcie_driver = { |
170 | .driver = { | 267 | .driver = { |
171 | .name = "layerscape-pcie", | 268 | .name = "layerscape-pcie", |