diff options
author | Bjorn Helgaas <bhelgaas@google.com> | 2018-08-15 15:59:10 -0400 |
---|---|---|
committer | Bjorn Helgaas <bhelgaas@google.com> | 2018-08-15 15:59:10 -0400 |
commit | 37f0e311bca66e9fe74703887c9bb4965914cabf (patch) | |
tree | 3be9f14dc28fe04e9823cc12d4bc11c9cf2a6af8 /drivers | |
parent | ce342a1aa8c61fe3315b782f6cf4f34d5babce13 (diff) | |
parent | eb1e39f784e83321353314da1b2e8cbb2fceaba9 (diff) |
Merge branch 'remotes/lorenzo/pci/cadence'
- Correct the Cadence cdns_pcie_writel() signature (Alan Douglas)
- Add Cadence support for optional generic PHYs (Alan Douglas)
- Add Cadence power management ops (Alan Douglas)
- Remove redundant variable from Cadence driver (Colin Ian King)
* remotes/lorenzo/pci/cadence:
PCI: pcie-cadence-ep: Remove redundant variable mmc
PCI: cadence: Add shutdown callback to host driver
PCI: cadence: Add Power Management ops for host and EP
dt-bindings: PCI: cadence: Add DT bindings for optional PHYs
PCI: cadence: Add generic PHY support to host and EP drivers
PCI: cadence: Update cdns_pcie_writel() function signature
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/pci/controller/pcie-cadence-ep.c | 18 | ||||
-rw-r--r-- | drivers/pci/controller/pcie-cadence-host.c | 33 | ||||
-rw-r--r-- | drivers/pci/controller/pcie-cadence.c | 123 | ||||
-rw-r--r-- | drivers/pci/controller/pcie-cadence.h | 13 |
4 files changed, 183 insertions, 4 deletions
diff --git a/drivers/pci/controller/pcie-cadence-ep.c b/drivers/pci/controller/pcie-cadence-ep.c index e3fe4124e3af..04fe76c3d59d 100644 --- a/drivers/pci/controller/pcie-cadence-ep.c +++ b/drivers/pci/controller/pcie-cadence-ep.c | |||
@@ -238,7 +238,7 @@ static int cdns_pcie_ep_get_msi(struct pci_epc *epc, u8 fn) | |||
238 | struct cdns_pcie_ep *ep = epc_get_drvdata(epc); | 238 | struct cdns_pcie_ep *ep = epc_get_drvdata(epc); |
239 | struct cdns_pcie *pcie = &ep->pcie; | 239 | struct cdns_pcie *pcie = &ep->pcie; |
240 | u32 cap = CDNS_PCIE_EP_FUNC_MSI_CAP_OFFSET; | 240 | u32 cap = CDNS_PCIE_EP_FUNC_MSI_CAP_OFFSET; |
241 | u16 flags, mmc, mme; | 241 | u16 flags, mme; |
242 | 242 | ||
243 | /* Validate that the MSI feature is actually enabled. */ | 243 | /* Validate that the MSI feature is actually enabled. */ |
244 | flags = cdns_pcie_ep_fn_readw(pcie, fn, cap + PCI_MSI_FLAGS); | 244 | flags = cdns_pcie_ep_fn_readw(pcie, fn, cap + PCI_MSI_FLAGS); |
@@ -249,7 +249,6 @@ static int cdns_pcie_ep_get_msi(struct pci_epc *epc, u8 fn) | |||
249 | * Get the Multiple Message Enable bitfield from the Message Control | 249 | * Get the Multiple Message Enable bitfield from the Message Control |
250 | * register. | 250 | * register. |
251 | */ | 251 | */ |
252 | mmc = (flags & PCI_MSI_FLAGS_QMASK) >> 1; | ||
253 | mme = (flags & PCI_MSI_FLAGS_QSIZE) >> 4; | 252 | mme = (flags & PCI_MSI_FLAGS_QSIZE) >> 4; |
254 | 253 | ||
255 | return mme; | 254 | return mme; |
@@ -439,6 +438,7 @@ static int cdns_pcie_ep_probe(struct platform_device *pdev) | |||
439 | struct pci_epc *epc; | 438 | struct pci_epc *epc; |
440 | struct resource *res; | 439 | struct resource *res; |
441 | int ret; | 440 | int ret; |
441 | int phy_count; | ||
442 | 442 | ||
443 | ep = devm_kzalloc(dev, sizeof(*ep), GFP_KERNEL); | 443 | ep = devm_kzalloc(dev, sizeof(*ep), GFP_KERNEL); |
444 | if (!ep) | 444 | if (!ep) |
@@ -473,6 +473,12 @@ static int cdns_pcie_ep_probe(struct platform_device *pdev) | |||
473 | if (!ep->ob_addr) | 473 | if (!ep->ob_addr) |
474 | return -ENOMEM; | 474 | return -ENOMEM; |
475 | 475 | ||
476 | ret = cdns_pcie_init_phy(dev, pcie); | ||
477 | if (ret) { | ||
478 | dev_err(dev, "failed to init phy\n"); | ||
479 | return ret; | ||
480 | } | ||
481 | platform_set_drvdata(pdev, pcie); | ||
476 | pm_runtime_enable(dev); | 482 | pm_runtime_enable(dev); |
477 | ret = pm_runtime_get_sync(dev); | 483 | ret = pm_runtime_get_sync(dev); |
478 | if (ret < 0) { | 484 | if (ret < 0) { |
@@ -521,6 +527,10 @@ static int cdns_pcie_ep_probe(struct platform_device *pdev) | |||
521 | 527 | ||
522 | err_get_sync: | 528 | err_get_sync: |
523 | pm_runtime_disable(dev); | 529 | pm_runtime_disable(dev); |
530 | cdns_pcie_disable_phy(pcie); | ||
531 | phy_count = pcie->phy_count; | ||
532 | while (phy_count--) | ||
533 | device_link_del(pcie->link[phy_count]); | ||
524 | 534 | ||
525 | return ret; | 535 | return ret; |
526 | } | 536 | } |
@@ -528,6 +538,7 @@ static int cdns_pcie_ep_probe(struct platform_device *pdev) | |||
528 | static void cdns_pcie_ep_shutdown(struct platform_device *pdev) | 538 | static void cdns_pcie_ep_shutdown(struct platform_device *pdev) |
529 | { | 539 | { |
530 | struct device *dev = &pdev->dev; | 540 | struct device *dev = &pdev->dev; |
541 | struct cdns_pcie *pcie = dev_get_drvdata(dev); | ||
531 | int ret; | 542 | int ret; |
532 | 543 | ||
533 | ret = pm_runtime_put_sync(dev); | 544 | ret = pm_runtime_put_sync(dev); |
@@ -536,13 +547,14 @@ static void cdns_pcie_ep_shutdown(struct platform_device *pdev) | |||
536 | 547 | ||
537 | pm_runtime_disable(dev); | 548 | pm_runtime_disable(dev); |
538 | 549 | ||
539 | /* The PCIe controller can't be disabled. */ | 550 | cdns_pcie_disable_phy(pcie); |
540 | } | 551 | } |
541 | 552 | ||
542 | static struct platform_driver cdns_pcie_ep_driver = { | 553 | static struct platform_driver cdns_pcie_ep_driver = { |
543 | .driver = { | 554 | .driver = { |
544 | .name = "cdns-pcie-ep", | 555 | .name = "cdns-pcie-ep", |
545 | .of_match_table = cdns_pcie_ep_of_match, | 556 | .of_match_table = cdns_pcie_ep_of_match, |
557 | .pm = &cdns_pcie_pm_ops, | ||
546 | }, | 558 | }, |
547 | .probe = cdns_pcie_ep_probe, | 559 | .probe = cdns_pcie_ep_probe, |
548 | .shutdown = cdns_pcie_ep_shutdown, | 560 | .shutdown = cdns_pcie_ep_shutdown, |
diff --git a/drivers/pci/controller/pcie-cadence-host.c b/drivers/pci/controller/pcie-cadence-host.c index a4ebbd37b553..ec394f6a19c8 100644 --- a/drivers/pci/controller/pcie-cadence-host.c +++ b/drivers/pci/controller/pcie-cadence-host.c | |||
@@ -58,6 +58,11 @@ static void __iomem *cdns_pci_map_bus(struct pci_bus *bus, unsigned int devfn, | |||
58 | 58 | ||
59 | return pcie->reg_base + (where & 0xfff); | 59 | return pcie->reg_base + (where & 0xfff); |
60 | } | 60 | } |
61 | /* Check that the link is up */ | ||
62 | if (!(cdns_pcie_readl(pcie, CDNS_PCIE_LM_BASE) & 0x1)) | ||
63 | return NULL; | ||
64 | /* Clear AXI link-down status */ | ||
65 | cdns_pcie_writel(pcie, CDNS_PCIE_AT_LINKDOWN, 0x0); | ||
61 | 66 | ||
62 | /* Update Output registers for AXI region 0. */ | 67 | /* Update Output registers for AXI region 0. */ |
63 | addr0 = CDNS_PCIE_AT_OB_REGION_PCI_ADDR0_NBITS(12) | | 68 | addr0 = CDNS_PCIE_AT_OB_REGION_PCI_ADDR0_NBITS(12) | |
@@ -239,6 +244,7 @@ static int cdns_pcie_host_probe(struct platform_device *pdev) | |||
239 | struct cdns_pcie *pcie; | 244 | struct cdns_pcie *pcie; |
240 | struct resource *res; | 245 | struct resource *res; |
241 | int ret; | 246 | int ret; |
247 | int phy_count; | ||
242 | 248 | ||
243 | bridge = devm_pci_alloc_host_bridge(dev, sizeof(*rc)); | 249 | bridge = devm_pci_alloc_host_bridge(dev, sizeof(*rc)); |
244 | if (!bridge) | 250 | if (!bridge) |
@@ -290,6 +296,13 @@ static int cdns_pcie_host_probe(struct platform_device *pdev) | |||
290 | } | 296 | } |
291 | pcie->mem_res = res; | 297 | pcie->mem_res = res; |
292 | 298 | ||
299 | ret = cdns_pcie_init_phy(dev, pcie); | ||
300 | if (ret) { | ||
301 | dev_err(dev, "failed to init phy\n"); | ||
302 | return ret; | ||
303 | } | ||
304 | platform_set_drvdata(pdev, pcie); | ||
305 | |||
293 | pm_runtime_enable(dev); | 306 | pm_runtime_enable(dev); |
294 | ret = pm_runtime_get_sync(dev); | 307 | ret = pm_runtime_get_sync(dev); |
295 | if (ret < 0) { | 308 | if (ret < 0) { |
@@ -322,15 +335,35 @@ static int cdns_pcie_host_probe(struct platform_device *pdev) | |||
322 | 335 | ||
323 | err_get_sync: | 336 | err_get_sync: |
324 | pm_runtime_disable(dev); | 337 | pm_runtime_disable(dev); |
338 | cdns_pcie_disable_phy(pcie); | ||
339 | phy_count = pcie->phy_count; | ||
340 | while (phy_count--) | ||
341 | device_link_del(pcie->link[phy_count]); | ||
325 | 342 | ||
326 | return ret; | 343 | return ret; |
327 | } | 344 | } |
328 | 345 | ||
346 | static void cdns_pcie_shutdown(struct platform_device *pdev) | ||
347 | { | ||
348 | struct device *dev = &pdev->dev; | ||
349 | struct cdns_pcie *pcie = dev_get_drvdata(dev); | ||
350 | int ret; | ||
351 | |||
352 | ret = pm_runtime_put_sync(dev); | ||
353 | if (ret < 0) | ||
354 | dev_dbg(dev, "pm_runtime_put_sync failed\n"); | ||
355 | |||
356 | pm_runtime_disable(dev); | ||
357 | cdns_pcie_disable_phy(pcie); | ||
358 | } | ||
359 | |||
329 | static struct platform_driver cdns_pcie_host_driver = { | 360 | static struct platform_driver cdns_pcie_host_driver = { |
330 | .driver = { | 361 | .driver = { |
331 | .name = "cdns-pcie-host", | 362 | .name = "cdns-pcie-host", |
332 | .of_match_table = cdns_pcie_host_of_match, | 363 | .of_match_table = cdns_pcie_host_of_match, |
364 | .pm = &cdns_pcie_pm_ops, | ||
333 | }, | 365 | }, |
334 | .probe = cdns_pcie_host_probe, | 366 | .probe = cdns_pcie_host_probe, |
367 | .shutdown = cdns_pcie_shutdown, | ||
335 | }; | 368 | }; |
336 | builtin_platform_driver(cdns_pcie_host_driver); | 369 | builtin_platform_driver(cdns_pcie_host_driver); |
diff --git a/drivers/pci/controller/pcie-cadence.c b/drivers/pci/controller/pcie-cadence.c index 138d113eb45d..86f1b002c846 100644 --- a/drivers/pci/controller/pcie-cadence.c +++ b/drivers/pci/controller/pcie-cadence.c | |||
@@ -124,3 +124,126 @@ void cdns_pcie_reset_outbound_region(struct cdns_pcie *pcie, u32 r) | |||
124 | cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_CPU_ADDR0(r), 0); | 124 | cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_CPU_ADDR0(r), 0); |
125 | cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_CPU_ADDR1(r), 0); | 125 | cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_CPU_ADDR1(r), 0); |
126 | } | 126 | } |
127 | |||
128 | void cdns_pcie_disable_phy(struct cdns_pcie *pcie) | ||
129 | { | ||
130 | int i = pcie->phy_count; | ||
131 | |||
132 | while (i--) { | ||
133 | phy_power_off(pcie->phy[i]); | ||
134 | phy_exit(pcie->phy[i]); | ||
135 | } | ||
136 | } | ||
137 | |||
138 | int cdns_pcie_enable_phy(struct cdns_pcie *pcie) | ||
139 | { | ||
140 | int ret; | ||
141 | int i; | ||
142 | |||
143 | for (i = 0; i < pcie->phy_count; i++) { | ||
144 | ret = phy_init(pcie->phy[i]); | ||
145 | if (ret < 0) | ||
146 | goto err_phy; | ||
147 | |||
148 | ret = phy_power_on(pcie->phy[i]); | ||
149 | if (ret < 0) { | ||
150 | phy_exit(pcie->phy[i]); | ||
151 | goto err_phy; | ||
152 | } | ||
153 | } | ||
154 | |||
155 | return 0; | ||
156 | |||
157 | err_phy: | ||
158 | while (--i >= 0) { | ||
159 | phy_power_off(pcie->phy[i]); | ||
160 | phy_exit(pcie->phy[i]); | ||
161 | } | ||
162 | |||
163 | return ret; | ||
164 | } | ||
165 | |||
166 | int cdns_pcie_init_phy(struct device *dev, struct cdns_pcie *pcie) | ||
167 | { | ||
168 | struct device_node *np = dev->of_node; | ||
169 | int phy_count; | ||
170 | struct phy **phy; | ||
171 | struct device_link **link; | ||
172 | int i; | ||
173 | int ret; | ||
174 | const char *name; | ||
175 | |||
176 | phy_count = of_property_count_strings(np, "phy-names"); | ||
177 | if (phy_count < 1) { | ||
178 | dev_err(dev, "no phy-names. PHY will not be initialized\n"); | ||
179 | pcie->phy_count = 0; | ||
180 | return 0; | ||
181 | } | ||
182 | |||
183 | phy = devm_kzalloc(dev, sizeof(*phy) * phy_count, GFP_KERNEL); | ||
184 | if (!phy) | ||
185 | return -ENOMEM; | ||
186 | |||
187 | link = devm_kzalloc(dev, sizeof(*link) * phy_count, GFP_KERNEL); | ||
188 | if (!link) | ||
189 | return -ENOMEM; | ||
190 | |||
191 | for (i = 0; i < phy_count; i++) { | ||
192 | of_property_read_string_index(np, "phy-names", i, &name); | ||
193 | phy[i] = devm_phy_optional_get(dev, name); | ||
194 | if (IS_ERR(phy)) | ||
195 | return PTR_ERR(phy); | ||
196 | |||
197 | link[i] = device_link_add(dev, &phy[i]->dev, DL_FLAG_STATELESS); | ||
198 | if (!link[i]) { | ||
199 | ret = -EINVAL; | ||
200 | goto err_link; | ||
201 | } | ||
202 | } | ||
203 | |||
204 | pcie->phy_count = phy_count; | ||
205 | pcie->phy = phy; | ||
206 | pcie->link = link; | ||
207 | |||
208 | ret = cdns_pcie_enable_phy(pcie); | ||
209 | if (ret) | ||
210 | goto err_link; | ||
211 | |||
212 | return 0; | ||
213 | |||
214 | err_link: | ||
215 | while (--i >= 0) | ||
216 | device_link_del(link[i]); | ||
217 | |||
218 | return ret; | ||
219 | } | ||
220 | |||
221 | #ifdef CONFIG_PM_SLEEP | ||
222 | static int cdns_pcie_suspend_noirq(struct device *dev) | ||
223 | { | ||
224 | struct cdns_pcie *pcie = dev_get_drvdata(dev); | ||
225 | |||
226 | cdns_pcie_disable_phy(pcie); | ||
227 | |||
228 | return 0; | ||
229 | } | ||
230 | |||
231 | static int cdns_pcie_resume_noirq(struct device *dev) | ||
232 | { | ||
233 | struct cdns_pcie *pcie = dev_get_drvdata(dev); | ||
234 | int ret; | ||
235 | |||
236 | ret = cdns_pcie_enable_phy(pcie); | ||
237 | if (ret) { | ||
238 | dev_err(dev, "failed to enable phy\n"); | ||
239 | return ret; | ||
240 | } | ||
241 | |||
242 | return 0; | ||
243 | } | ||
244 | #endif | ||
245 | |||
246 | const struct dev_pm_ops cdns_pcie_pm_ops = { | ||
247 | SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(cdns_pcie_suspend_noirq, | ||
248 | cdns_pcie_resume_noirq) | ||
249 | }; | ||
diff --git a/drivers/pci/controller/pcie-cadence.h b/drivers/pci/controller/pcie-cadence.h index 4bb27333b05c..ae6bf2a2b3d3 100644 --- a/drivers/pci/controller/pcie-cadence.h +++ b/drivers/pci/controller/pcie-cadence.h | |||
@@ -8,6 +8,7 @@ | |||
8 | 8 | ||
9 | #include <linux/kernel.h> | 9 | #include <linux/kernel.h> |
10 | #include <linux/pci.h> | 10 | #include <linux/pci.h> |
11 | #include <linux/phy/phy.h> | ||
11 | 12 | ||
12 | /* | 13 | /* |
13 | * Local Management Registers | 14 | * Local Management Registers |
@@ -165,6 +166,9 @@ | |||
165 | #define CDNS_PCIE_AT_IB_RP_BAR_ADDR1(bar) \ | 166 | #define CDNS_PCIE_AT_IB_RP_BAR_ADDR1(bar) \ |
166 | (CDNS_PCIE_AT_BASE + 0x0804 + (bar) * 0x0008) | 167 | (CDNS_PCIE_AT_BASE + 0x0804 + (bar) * 0x0008) |
167 | 168 | ||
169 | /* AXI link down register */ | ||
170 | #define CDNS_PCIE_AT_LINKDOWN (CDNS_PCIE_AT_BASE + 0x0824) | ||
171 | |||
168 | enum cdns_pcie_rp_bar { | 172 | enum cdns_pcie_rp_bar { |
169 | RP_BAR0, | 173 | RP_BAR0, |
170 | RP_BAR1, | 174 | RP_BAR1, |
@@ -229,6 +233,9 @@ struct cdns_pcie { | |||
229 | struct resource *mem_res; | 233 | struct resource *mem_res; |
230 | bool is_rc; | 234 | bool is_rc; |
231 | u8 bus; | 235 | u8 bus; |
236 | int phy_count; | ||
237 | struct phy **phy; | ||
238 | struct device_link **link; | ||
232 | }; | 239 | }; |
233 | 240 | ||
234 | /* Register access */ | 241 | /* Register access */ |
@@ -279,7 +286,7 @@ static inline void cdns_pcie_ep_fn_writew(struct cdns_pcie *pcie, u8 fn, | |||
279 | } | 286 | } |
280 | 287 | ||
281 | static inline void cdns_pcie_ep_fn_writel(struct cdns_pcie *pcie, u8 fn, | 288 | static inline void cdns_pcie_ep_fn_writel(struct cdns_pcie *pcie, u8 fn, |
282 | u32 reg, u16 value) | 289 | u32 reg, u32 value) |
283 | { | 290 | { |
284 | writel(value, pcie->reg_base + CDNS_PCIE_EP_FUNC_BASE(fn) + reg); | 291 | writel(value, pcie->reg_base + CDNS_PCIE_EP_FUNC_BASE(fn) + reg); |
285 | } | 292 | } |
@@ -307,5 +314,9 @@ void cdns_pcie_set_outbound_region_for_normal_msg(struct cdns_pcie *pcie, u8 fn, | |||
307 | u32 r, u64 cpu_addr); | 314 | u32 r, u64 cpu_addr); |
308 | 315 | ||
309 | void cdns_pcie_reset_outbound_region(struct cdns_pcie *pcie, u32 r); | 316 | void cdns_pcie_reset_outbound_region(struct cdns_pcie *pcie, u32 r); |
317 | void cdns_pcie_disable_phy(struct cdns_pcie *pcie); | ||
318 | int cdns_pcie_enable_phy(struct cdns_pcie *pcie); | ||
319 | int cdns_pcie_init_phy(struct device *dev, struct cdns_pcie *pcie); | ||
320 | extern const struct dev_pm_ops cdns_pcie_pm_ops; | ||
310 | 321 | ||
311 | #endif /* _PCIE_CADENCE_H */ | 322 | #endif /* _PCIE_CADENCE_H */ |