1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Copyright (C) 2015 Broadcom Corporation |
4 | */ |
5 | |
6 | #include <linux/kernel.h> |
7 | #include <linux/pci.h> |
8 | #include <linux/clk.h> |
9 | #include <linux/module.h> |
10 | #include <linux/slab.h> |
11 | #include <linux/interrupt.h> |
12 | #include <linux/platform_device.h> |
13 | #include <linux/of_address.h> |
14 | #include <linux/of_pci.h> |
15 | #include <linux/of_platform.h> |
16 | #include <linux/phy/phy.h> |
17 | |
18 | #include "../pci.h" |
19 | #include "pcie-iproc.h" |
20 | |
21 | static const struct of_device_id iproc_pcie_of_match_table[] = { |
22 | { |
23 | .compatible = "brcm,iproc-pcie" , |
24 | .data = (int *)IPROC_PCIE_PAXB, |
25 | }, { |
26 | .compatible = "brcm,iproc-pcie-paxb-v2" , |
27 | .data = (int *)IPROC_PCIE_PAXB_V2, |
28 | }, { |
29 | .compatible = "brcm,iproc-pcie-paxc" , |
30 | .data = (int *)IPROC_PCIE_PAXC, |
31 | }, { |
32 | .compatible = "brcm,iproc-pcie-paxc-v2" , |
33 | .data = (int *)IPROC_PCIE_PAXC_V2, |
34 | }, |
35 | { /* sentinel */ } |
36 | }; |
37 | MODULE_DEVICE_TABLE(of, iproc_pcie_of_match_table); |
38 | |
39 | static int iproc_pltfm_pcie_probe(struct platform_device *pdev) |
40 | { |
41 | struct device *dev = &pdev->dev; |
42 | struct iproc_pcie *pcie; |
43 | struct device_node *np = dev->of_node; |
44 | struct resource reg; |
45 | struct pci_host_bridge *bridge; |
46 | int ret; |
47 | |
48 | bridge = devm_pci_alloc_host_bridge(dev, priv: sizeof(*pcie)); |
49 | if (!bridge) |
50 | return -ENOMEM; |
51 | |
52 | pcie = pci_host_bridge_priv(bridge); |
53 | |
54 | pcie->dev = dev; |
55 | pcie->type = (uintptr_t)of_device_get_match_data(dev); |
56 | |
57 | ret = of_address_to_resource(dev: np, index: 0, r: ®); |
58 | if (ret < 0) { |
59 | dev_err(dev, "unable to obtain controller resources\n" ); |
60 | return ret; |
61 | } |
62 | |
63 | pcie->base = devm_pci_remap_cfgspace(dev, offset: reg.start, |
64 | size: resource_size(res: ®)); |
65 | if (!pcie->base) { |
66 | dev_err(dev, "unable to map controller registers\n" ); |
67 | return -ENOMEM; |
68 | } |
69 | pcie->base_addr = reg.start; |
70 | |
71 | if (of_property_read_bool(np, propname: "brcm,pcie-ob" )) { |
72 | u32 val; |
73 | |
74 | ret = of_property_read_u32(np, propname: "brcm,pcie-ob-axi-offset" , |
75 | out_value: &val); |
76 | if (ret) { |
77 | dev_err(dev, |
78 | "missing brcm,pcie-ob-axi-offset property\n" ); |
79 | return ret; |
80 | } |
81 | pcie->ob.axi_offset = val; |
82 | pcie->need_ob_cfg = true; |
83 | } |
84 | |
85 | /* |
86 | * DT nodes are not used by all platforms that use the iProc PCIe |
87 | * core driver. For platforms that require explicit inbound mapping |
88 | * configuration, "dma-ranges" would have been present in DT |
89 | */ |
90 | pcie->need_ib_cfg = of_property_read_bool(np, propname: "dma-ranges" ); |
91 | |
92 | /* PHY use is optional */ |
93 | pcie->phy = devm_phy_optional_get(dev, string: "pcie-phy" ); |
94 | if (IS_ERR(ptr: pcie->phy)) |
95 | return PTR_ERR(ptr: pcie->phy); |
96 | |
97 | /* PAXC doesn't support legacy IRQs, skip mapping */ |
98 | switch (pcie->type) { |
99 | case IPROC_PCIE_PAXC: |
100 | case IPROC_PCIE_PAXC_V2: |
101 | pcie->map_irq = NULL; |
102 | break; |
103 | default: |
104 | break; |
105 | } |
106 | |
107 | ret = iproc_pcie_setup(pcie, res: &bridge->windows); |
108 | if (ret) { |
109 | dev_err(dev, "PCIe controller setup failed\n" ); |
110 | return ret; |
111 | } |
112 | |
113 | platform_set_drvdata(pdev, data: pcie); |
114 | return 0; |
115 | } |
116 | |
117 | static void iproc_pltfm_pcie_remove(struct platform_device *pdev) |
118 | { |
119 | struct iproc_pcie *pcie = platform_get_drvdata(pdev); |
120 | |
121 | iproc_pcie_remove(pcie); |
122 | } |
123 | |
124 | static void iproc_pltfm_pcie_shutdown(struct platform_device *pdev) |
125 | { |
126 | struct iproc_pcie *pcie = platform_get_drvdata(pdev); |
127 | |
128 | iproc_pcie_shutdown(pcie); |
129 | } |
130 | |
131 | static struct platform_driver iproc_pltfm_pcie_driver = { |
132 | .driver = { |
133 | .name = "iproc-pcie" , |
134 | .of_match_table = of_match_ptr(iproc_pcie_of_match_table), |
135 | }, |
136 | .probe = iproc_pltfm_pcie_probe, |
137 | .remove_new = iproc_pltfm_pcie_remove, |
138 | .shutdown = iproc_pltfm_pcie_shutdown, |
139 | }; |
140 | module_platform_driver(iproc_pltfm_pcie_driver); |
141 | |
142 | MODULE_AUTHOR("Ray Jui <rjui@broadcom.com>" ); |
143 | MODULE_DESCRIPTION("Broadcom iPROC PCIe platform driver" ); |
144 | MODULE_LICENSE("GPL v2" ); |
145 | |