diff options
author | Alan Douglas <adouglas@cadence.com> | 2018-06-25 04:30:50 -0400 |
---|---|---|
committer | Lorenzo Pieralisi <lorenzo.pieralisi@arm.com> | 2018-07-11 05:39:39 -0400 |
commit | dfb80534692ddc5b97e1da4384f13dc0287fccb2 (patch) | |
tree | 7d565642e9c5fce2567d5f43db48288647f864e7 | |
parent | 7e37dc1db594d5a4ed062dbaf51ef89596a9df8a (diff) |
PCI: cadence: Add generic PHY support to host and EP drivers
If PHYs are present, initialize and enable them at driver probe.
Signed-off-by: Alan Douglas <adouglas@cadence.com>
[lorenzo.pieralisi@arm.com: updated commit log]
Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
-rw-r--r-- | drivers/pci/controller/pcie-cadence-ep.c | 14 | ||||
-rw-r--r-- | drivers/pci/controller/pcie-cadence-host.c | 15 | ||||
-rw-r--r-- | drivers/pci/controller/pcie-cadence.c | 93 | ||||
-rw-r--r-- | drivers/pci/controller/pcie-cadence.h | 7 |
4 files changed, 128 insertions, 1 deletions
diff --git a/drivers/pci/controller/pcie-cadence-ep.c b/drivers/pci/controller/pcie-cadence-ep.c index e3fe4124e3af..c02f33d8e506 100644 --- a/drivers/pci/controller/pcie-cadence-ep.c +++ b/drivers/pci/controller/pcie-cadence-ep.c | |||
@@ -439,6 +439,7 @@ static int cdns_pcie_ep_probe(struct platform_device *pdev) | |||
439 | struct pci_epc *epc; | 439 | struct pci_epc *epc; |
440 | struct resource *res; | 440 | struct resource *res; |
441 | int ret; | 441 | int ret; |
442 | int phy_count; | ||
442 | 443 | ||
443 | ep = devm_kzalloc(dev, sizeof(*ep), GFP_KERNEL); | 444 | ep = devm_kzalloc(dev, sizeof(*ep), GFP_KERNEL); |
444 | if (!ep) | 445 | if (!ep) |
@@ -473,6 +474,12 @@ static int cdns_pcie_ep_probe(struct platform_device *pdev) | |||
473 | if (!ep->ob_addr) | 474 | if (!ep->ob_addr) |
474 | return -ENOMEM; | 475 | return -ENOMEM; |
475 | 476 | ||
477 | ret = cdns_pcie_init_phy(dev, pcie); | ||
478 | if (ret) { | ||
479 | dev_err(dev, "failed to init phy\n"); | ||
480 | return ret; | ||
481 | } | ||
482 | platform_set_drvdata(pdev, pcie); | ||
476 | pm_runtime_enable(dev); | 483 | pm_runtime_enable(dev); |
477 | ret = pm_runtime_get_sync(dev); | 484 | ret = pm_runtime_get_sync(dev); |
478 | if (ret < 0) { | 485 | if (ret < 0) { |
@@ -521,6 +528,10 @@ static int cdns_pcie_ep_probe(struct platform_device *pdev) | |||
521 | 528 | ||
522 | err_get_sync: | 529 | err_get_sync: |
523 | pm_runtime_disable(dev); | 530 | pm_runtime_disable(dev); |
531 | cdns_pcie_disable_phy(pcie); | ||
532 | phy_count = pcie->phy_count; | ||
533 | while (phy_count--) | ||
534 | device_link_del(pcie->link[phy_count]); | ||
524 | 535 | ||
525 | return ret; | 536 | return ret; |
526 | } | 537 | } |
@@ -528,6 +539,7 @@ static int cdns_pcie_ep_probe(struct platform_device *pdev) | |||
528 | static void cdns_pcie_ep_shutdown(struct platform_device *pdev) | 539 | static void cdns_pcie_ep_shutdown(struct platform_device *pdev) |
529 | { | 540 | { |
530 | struct device *dev = &pdev->dev; | 541 | struct device *dev = &pdev->dev; |
542 | struct cdns_pcie *pcie = dev_get_drvdata(dev); | ||
531 | int ret; | 543 | int ret; |
532 | 544 | ||
533 | ret = pm_runtime_put_sync(dev); | 545 | ret = pm_runtime_put_sync(dev); |
@@ -536,7 +548,7 @@ static void cdns_pcie_ep_shutdown(struct platform_device *pdev) | |||
536 | 548 | ||
537 | pm_runtime_disable(dev); | 549 | pm_runtime_disable(dev); |
538 | 550 | ||
539 | /* The PCIe controller can't be disabled. */ | 551 | cdns_pcie_disable_phy(pcie); |
540 | } | 552 | } |
541 | 553 | ||
542 | static struct platform_driver cdns_pcie_ep_driver = { | 554 | static struct platform_driver cdns_pcie_ep_driver = { |
diff --git a/drivers/pci/controller/pcie-cadence-host.c b/drivers/pci/controller/pcie-cadence-host.c index a4ebbd37b553..36f31092562f 100644 --- a/drivers/pci/controller/pcie-cadence-host.c +++ b/drivers/pci/controller/pcie-cadence-host.c | |||
@@ -58,6 +58,9 @@ 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; | ||
61 | 64 | ||
62 | /* Update Output registers for AXI region 0. */ | 65 | /* Update Output registers for AXI region 0. */ |
63 | addr0 = CDNS_PCIE_AT_OB_REGION_PCI_ADDR0_NBITS(12) | | 66 | addr0 = CDNS_PCIE_AT_OB_REGION_PCI_ADDR0_NBITS(12) | |
@@ -239,6 +242,7 @@ static int cdns_pcie_host_probe(struct platform_device *pdev) | |||
239 | struct cdns_pcie *pcie; | 242 | struct cdns_pcie *pcie; |
240 | struct resource *res; | 243 | struct resource *res; |
241 | int ret; | 244 | int ret; |
245 | int phy_count; | ||
242 | 246 | ||
243 | bridge = devm_pci_alloc_host_bridge(dev, sizeof(*rc)); | 247 | bridge = devm_pci_alloc_host_bridge(dev, sizeof(*rc)); |
244 | if (!bridge) | 248 | if (!bridge) |
@@ -290,6 +294,13 @@ static int cdns_pcie_host_probe(struct platform_device *pdev) | |||
290 | } | 294 | } |
291 | pcie->mem_res = res; | 295 | pcie->mem_res = res; |
292 | 296 | ||
297 | ret = cdns_pcie_init_phy(dev, pcie); | ||
298 | if (ret) { | ||
299 | dev_err(dev, "failed to init phy\n"); | ||
300 | return ret; | ||
301 | } | ||
302 | platform_set_drvdata(pdev, pcie); | ||
303 | |||
293 | pm_runtime_enable(dev); | 304 | pm_runtime_enable(dev); |
294 | ret = pm_runtime_get_sync(dev); | 305 | ret = pm_runtime_get_sync(dev); |
295 | if (ret < 0) { | 306 | if (ret < 0) { |
@@ -322,6 +333,10 @@ static int cdns_pcie_host_probe(struct platform_device *pdev) | |||
322 | 333 | ||
323 | err_get_sync: | 334 | err_get_sync: |
324 | pm_runtime_disable(dev); | 335 | pm_runtime_disable(dev); |
336 | cdns_pcie_disable_phy(pcie); | ||
337 | phy_count = pcie->phy_count; | ||
338 | while (phy_count--) | ||
339 | device_link_del(pcie->link[phy_count]); | ||
325 | 340 | ||
326 | return ret; | 341 | return ret; |
327 | } | 342 | } |
diff --git a/drivers/pci/controller/pcie-cadence.c b/drivers/pci/controller/pcie-cadence.c index 138d113eb45d..2edc12661e44 100644 --- a/drivers/pci/controller/pcie-cadence.c +++ b/drivers/pci/controller/pcie-cadence.c | |||
@@ -124,3 +124,96 @@ 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 | } | ||
diff --git a/drivers/pci/controller/pcie-cadence.h b/drivers/pci/controller/pcie-cadence.h index ed336cc7f4ba..b342c808f7d6 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 |
@@ -229,6 +230,9 @@ struct cdns_pcie { | |||
229 | struct resource *mem_res; | 230 | struct resource *mem_res; |
230 | bool is_rc; | 231 | bool is_rc; |
231 | u8 bus; | 232 | u8 bus; |
233 | int phy_count; | ||
234 | struct phy **phy; | ||
235 | struct device_link **link; | ||
232 | }; | 236 | }; |
233 | 237 | ||
234 | /* Register access */ | 238 | /* Register access */ |
@@ -307,5 +311,8 @@ void cdns_pcie_set_outbound_region_for_normal_msg(struct cdns_pcie *pcie, u8 fn, | |||
307 | u32 r, u64 cpu_addr); | 311 | u32 r, u64 cpu_addr); |
308 | 312 | ||
309 | void cdns_pcie_reset_outbound_region(struct cdns_pcie *pcie, u32 r); | 313 | void cdns_pcie_reset_outbound_region(struct cdns_pcie *pcie, u32 r); |
314 | void cdns_pcie_disable_phy(struct cdns_pcie *pcie); | ||
315 | int cdns_pcie_enable_phy(struct cdns_pcie *pcie); | ||
316 | int cdns_pcie_init_phy(struct device *dev, struct cdns_pcie *pcie); | ||
310 | 317 | ||
311 | #endif /* _PCIE_CADENCE_H */ | 318 | #endif /* _PCIE_CADENCE_H */ |