diff options
Diffstat (limited to 'drivers/pci/controller/dwc/pcie-designware-ep.c')
-rw-r--r-- | drivers/pci/controller/dwc/pcie-designware-ep.c | 422 |
1 files changed, 422 insertions, 0 deletions
diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c new file mode 100644 index 000000000000..1eec4415a77f --- /dev/null +++ b/drivers/pci/controller/dwc/pcie-designware-ep.c | |||
@@ -0,0 +1,422 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /** | ||
3 | * Synopsys DesignWare PCIe Endpoint controller driver | ||
4 | * | ||
5 | * Copyright (C) 2017 Texas Instruments | ||
6 | * Author: Kishon Vijay Abraham I <kishon@ti.com> | ||
7 | */ | ||
8 | |||
9 | #include <linux/of.h> | ||
10 | |||
11 | #include "pcie-designware.h" | ||
12 | #include <linux/pci-epc.h> | ||
13 | #include <linux/pci-epf.h> | ||
14 | |||
15 | void dw_pcie_ep_linkup(struct dw_pcie_ep *ep) | ||
16 | { | ||
17 | struct pci_epc *epc = ep->epc; | ||
18 | |||
19 | pci_epc_linkup(epc); | ||
20 | } | ||
21 | |||
22 | static void __dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar, | ||
23 | int flags) | ||
24 | { | ||
25 | u32 reg; | ||
26 | |||
27 | reg = PCI_BASE_ADDRESS_0 + (4 * bar); | ||
28 | dw_pcie_dbi_ro_wr_en(pci); | ||
29 | dw_pcie_writel_dbi2(pci, reg, 0x0); | ||
30 | dw_pcie_writel_dbi(pci, reg, 0x0); | ||
31 | if (flags & PCI_BASE_ADDRESS_MEM_TYPE_64) { | ||
32 | dw_pcie_writel_dbi2(pci, reg + 4, 0x0); | ||
33 | dw_pcie_writel_dbi(pci, reg + 4, 0x0); | ||
34 | } | ||
35 | dw_pcie_dbi_ro_wr_dis(pci); | ||
36 | } | ||
37 | |||
38 | void dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar) | ||
39 | { | ||
40 | __dw_pcie_ep_reset_bar(pci, bar, 0); | ||
41 | } | ||
42 | |||
43 | static int dw_pcie_ep_write_header(struct pci_epc *epc, u8 func_no, | ||
44 | struct pci_epf_header *hdr) | ||
45 | { | ||
46 | struct dw_pcie_ep *ep = epc_get_drvdata(epc); | ||
47 | struct dw_pcie *pci = to_dw_pcie_from_ep(ep); | ||
48 | |||
49 | dw_pcie_dbi_ro_wr_en(pci); | ||
50 | dw_pcie_writew_dbi(pci, PCI_VENDOR_ID, hdr->vendorid); | ||
51 | dw_pcie_writew_dbi(pci, PCI_DEVICE_ID, hdr->deviceid); | ||
52 | dw_pcie_writeb_dbi(pci, PCI_REVISION_ID, hdr->revid); | ||
53 | dw_pcie_writeb_dbi(pci, PCI_CLASS_PROG, hdr->progif_code); | ||
54 | dw_pcie_writew_dbi(pci, PCI_CLASS_DEVICE, | ||
55 | hdr->subclass_code | hdr->baseclass_code << 8); | ||
56 | dw_pcie_writeb_dbi(pci, PCI_CACHE_LINE_SIZE, | ||
57 | hdr->cache_line_size); | ||
58 | dw_pcie_writew_dbi(pci, PCI_SUBSYSTEM_VENDOR_ID, | ||
59 | hdr->subsys_vendor_id); | ||
60 | dw_pcie_writew_dbi(pci, PCI_SUBSYSTEM_ID, hdr->subsys_id); | ||
61 | dw_pcie_writeb_dbi(pci, PCI_INTERRUPT_PIN, | ||
62 | hdr->interrupt_pin); | ||
63 | dw_pcie_dbi_ro_wr_dis(pci); | ||
64 | |||
65 | return 0; | ||
66 | } | ||
67 | |||
68 | static int dw_pcie_ep_inbound_atu(struct dw_pcie_ep *ep, enum pci_barno bar, | ||
69 | dma_addr_t cpu_addr, | ||
70 | enum dw_pcie_as_type as_type) | ||
71 | { | ||
72 | int ret; | ||
73 | u32 free_win; | ||
74 | struct dw_pcie *pci = to_dw_pcie_from_ep(ep); | ||
75 | |||
76 | free_win = find_first_zero_bit(ep->ib_window_map, ep->num_ib_windows); | ||
77 | if (free_win >= ep->num_ib_windows) { | ||
78 | dev_err(pci->dev, "No free inbound window\n"); | ||
79 | return -EINVAL; | ||
80 | } | ||
81 | |||
82 | ret = dw_pcie_prog_inbound_atu(pci, free_win, bar, cpu_addr, | ||
83 | as_type); | ||
84 | if (ret < 0) { | ||
85 | dev_err(pci->dev, "Failed to program IB window\n"); | ||
86 | return ret; | ||
87 | } | ||
88 | |||
89 | ep->bar_to_atu[bar] = free_win; | ||
90 | set_bit(free_win, ep->ib_window_map); | ||
91 | |||
92 | return 0; | ||
93 | } | ||
94 | |||
95 | static int dw_pcie_ep_outbound_atu(struct dw_pcie_ep *ep, phys_addr_t phys_addr, | ||
96 | u64 pci_addr, size_t size) | ||
97 | { | ||
98 | u32 free_win; | ||
99 | struct dw_pcie *pci = to_dw_pcie_from_ep(ep); | ||
100 | |||
101 | free_win = find_first_zero_bit(ep->ob_window_map, ep->num_ob_windows); | ||
102 | if (free_win >= ep->num_ob_windows) { | ||
103 | dev_err(pci->dev, "No free outbound window\n"); | ||
104 | return -EINVAL; | ||
105 | } | ||
106 | |||
107 | dw_pcie_prog_outbound_atu(pci, free_win, PCIE_ATU_TYPE_MEM, | ||
108 | phys_addr, pci_addr, size); | ||
109 | |||
110 | set_bit(free_win, ep->ob_window_map); | ||
111 | ep->outbound_addr[free_win] = phys_addr; | ||
112 | |||
113 | return 0; | ||
114 | } | ||
115 | |||
116 | static void dw_pcie_ep_clear_bar(struct pci_epc *epc, u8 func_no, | ||
117 | struct pci_epf_bar *epf_bar) | ||
118 | { | ||
119 | struct dw_pcie_ep *ep = epc_get_drvdata(epc); | ||
120 | struct dw_pcie *pci = to_dw_pcie_from_ep(ep); | ||
121 | enum pci_barno bar = epf_bar->barno; | ||
122 | u32 atu_index = ep->bar_to_atu[bar]; | ||
123 | |||
124 | __dw_pcie_ep_reset_bar(pci, bar, epf_bar->flags); | ||
125 | |||
126 | dw_pcie_disable_atu(pci, atu_index, DW_PCIE_REGION_INBOUND); | ||
127 | clear_bit(atu_index, ep->ib_window_map); | ||
128 | } | ||
129 | |||
130 | static int dw_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no, | ||
131 | struct pci_epf_bar *epf_bar) | ||
132 | { | ||
133 | int ret; | ||
134 | struct dw_pcie_ep *ep = epc_get_drvdata(epc); | ||
135 | struct dw_pcie *pci = to_dw_pcie_from_ep(ep); | ||
136 | enum pci_barno bar = epf_bar->barno; | ||
137 | size_t size = epf_bar->size; | ||
138 | int flags = epf_bar->flags; | ||
139 | enum dw_pcie_as_type as_type; | ||
140 | u32 reg = PCI_BASE_ADDRESS_0 + (4 * bar); | ||
141 | |||
142 | if (!(flags & PCI_BASE_ADDRESS_SPACE)) | ||
143 | as_type = DW_PCIE_AS_MEM; | ||
144 | else | ||
145 | as_type = DW_PCIE_AS_IO; | ||
146 | |||
147 | ret = dw_pcie_ep_inbound_atu(ep, bar, epf_bar->phys_addr, as_type); | ||
148 | if (ret) | ||
149 | return ret; | ||
150 | |||
151 | dw_pcie_dbi_ro_wr_en(pci); | ||
152 | |||
153 | dw_pcie_writel_dbi2(pci, reg, lower_32_bits(size - 1)); | ||
154 | dw_pcie_writel_dbi(pci, reg, flags); | ||
155 | |||
156 | if (flags & PCI_BASE_ADDRESS_MEM_TYPE_64) { | ||
157 | dw_pcie_writel_dbi2(pci, reg + 4, upper_32_bits(size - 1)); | ||
158 | dw_pcie_writel_dbi(pci, reg + 4, 0); | ||
159 | } | ||
160 | |||
161 | dw_pcie_dbi_ro_wr_dis(pci); | ||
162 | |||
163 | return 0; | ||
164 | } | ||
165 | |||
166 | static int dw_pcie_find_index(struct dw_pcie_ep *ep, phys_addr_t addr, | ||
167 | u32 *atu_index) | ||
168 | { | ||
169 | u32 index; | ||
170 | |||
171 | for (index = 0; index < ep->num_ob_windows; index++) { | ||
172 | if (ep->outbound_addr[index] != addr) | ||
173 | continue; | ||
174 | *atu_index = index; | ||
175 | return 0; | ||
176 | } | ||
177 | |||
178 | return -EINVAL; | ||
179 | } | ||
180 | |||
181 | static void dw_pcie_ep_unmap_addr(struct pci_epc *epc, u8 func_no, | ||
182 | phys_addr_t addr) | ||
183 | { | ||
184 | int ret; | ||
185 | u32 atu_index; | ||
186 | struct dw_pcie_ep *ep = epc_get_drvdata(epc); | ||
187 | struct dw_pcie *pci = to_dw_pcie_from_ep(ep); | ||
188 | |||
189 | ret = dw_pcie_find_index(ep, addr, &atu_index); | ||
190 | if (ret < 0) | ||
191 | return; | ||
192 | |||
193 | dw_pcie_disable_atu(pci, atu_index, DW_PCIE_REGION_OUTBOUND); | ||
194 | clear_bit(atu_index, ep->ob_window_map); | ||
195 | } | ||
196 | |||
197 | static int dw_pcie_ep_map_addr(struct pci_epc *epc, u8 func_no, | ||
198 | phys_addr_t addr, | ||
199 | u64 pci_addr, size_t size) | ||
200 | { | ||
201 | int ret; | ||
202 | struct dw_pcie_ep *ep = epc_get_drvdata(epc); | ||
203 | struct dw_pcie *pci = to_dw_pcie_from_ep(ep); | ||
204 | |||
205 | ret = dw_pcie_ep_outbound_atu(ep, addr, pci_addr, size); | ||
206 | if (ret) { | ||
207 | dev_err(pci->dev, "Failed to enable address\n"); | ||
208 | return ret; | ||
209 | } | ||
210 | |||
211 | return 0; | ||
212 | } | ||
213 | |||
214 | static int dw_pcie_ep_get_msi(struct pci_epc *epc, u8 func_no) | ||
215 | { | ||
216 | int val; | ||
217 | struct dw_pcie_ep *ep = epc_get_drvdata(epc); | ||
218 | struct dw_pcie *pci = to_dw_pcie_from_ep(ep); | ||
219 | |||
220 | val = dw_pcie_readw_dbi(pci, MSI_MESSAGE_CONTROL); | ||
221 | if (!(val & MSI_CAP_MSI_EN_MASK)) | ||
222 | return -EINVAL; | ||
223 | |||
224 | val = (val & MSI_CAP_MME_MASK) >> MSI_CAP_MME_SHIFT; | ||
225 | return val; | ||
226 | } | ||
227 | |||
228 | static int dw_pcie_ep_set_msi(struct pci_epc *epc, u8 func_no, u8 encode_int) | ||
229 | { | ||
230 | int val; | ||
231 | struct dw_pcie_ep *ep = epc_get_drvdata(epc); | ||
232 | struct dw_pcie *pci = to_dw_pcie_from_ep(ep); | ||
233 | |||
234 | val = dw_pcie_readw_dbi(pci, MSI_MESSAGE_CONTROL); | ||
235 | val &= ~MSI_CAP_MMC_MASK; | ||
236 | val |= (encode_int << MSI_CAP_MMC_SHIFT) & MSI_CAP_MMC_MASK; | ||
237 | dw_pcie_dbi_ro_wr_en(pci); | ||
238 | dw_pcie_writew_dbi(pci, MSI_MESSAGE_CONTROL, val); | ||
239 | dw_pcie_dbi_ro_wr_dis(pci); | ||
240 | |||
241 | return 0; | ||
242 | } | ||
243 | |||
244 | static int dw_pcie_ep_raise_irq(struct pci_epc *epc, u8 func_no, | ||
245 | enum pci_epc_irq_type type, u8 interrupt_num) | ||
246 | { | ||
247 | struct dw_pcie_ep *ep = epc_get_drvdata(epc); | ||
248 | |||
249 | if (!ep->ops->raise_irq) | ||
250 | return -EINVAL; | ||
251 | |||
252 | return ep->ops->raise_irq(ep, func_no, type, interrupt_num); | ||
253 | } | ||
254 | |||
255 | static void dw_pcie_ep_stop(struct pci_epc *epc) | ||
256 | { | ||
257 | struct dw_pcie_ep *ep = epc_get_drvdata(epc); | ||
258 | struct dw_pcie *pci = to_dw_pcie_from_ep(ep); | ||
259 | |||
260 | if (!pci->ops->stop_link) | ||
261 | return; | ||
262 | |||
263 | pci->ops->stop_link(pci); | ||
264 | } | ||
265 | |||
266 | static int dw_pcie_ep_start(struct pci_epc *epc) | ||
267 | { | ||
268 | struct dw_pcie_ep *ep = epc_get_drvdata(epc); | ||
269 | struct dw_pcie *pci = to_dw_pcie_from_ep(ep); | ||
270 | |||
271 | if (!pci->ops->start_link) | ||
272 | return -EINVAL; | ||
273 | |||
274 | return pci->ops->start_link(pci); | ||
275 | } | ||
276 | |||
277 | static const struct pci_epc_ops epc_ops = { | ||
278 | .write_header = dw_pcie_ep_write_header, | ||
279 | .set_bar = dw_pcie_ep_set_bar, | ||
280 | .clear_bar = dw_pcie_ep_clear_bar, | ||
281 | .map_addr = dw_pcie_ep_map_addr, | ||
282 | .unmap_addr = dw_pcie_ep_unmap_addr, | ||
283 | .set_msi = dw_pcie_ep_set_msi, | ||
284 | .get_msi = dw_pcie_ep_get_msi, | ||
285 | .raise_irq = dw_pcie_ep_raise_irq, | ||
286 | .start = dw_pcie_ep_start, | ||
287 | .stop = dw_pcie_ep_stop, | ||
288 | }; | ||
289 | |||
290 | int dw_pcie_ep_raise_msi_irq(struct dw_pcie_ep *ep, u8 func_no, | ||
291 | u8 interrupt_num) | ||
292 | { | ||
293 | struct dw_pcie *pci = to_dw_pcie_from_ep(ep); | ||
294 | struct pci_epc *epc = ep->epc; | ||
295 | u16 msg_ctrl, msg_data; | ||
296 | u32 msg_addr_lower, msg_addr_upper; | ||
297 | u64 msg_addr; | ||
298 | bool has_upper; | ||
299 | int ret; | ||
300 | |||
301 | /* Raise MSI per the PCI Local Bus Specification Revision 3.0, 6.8.1. */ | ||
302 | msg_ctrl = dw_pcie_readw_dbi(pci, MSI_MESSAGE_CONTROL); | ||
303 | has_upper = !!(msg_ctrl & PCI_MSI_FLAGS_64BIT); | ||
304 | msg_addr_lower = dw_pcie_readl_dbi(pci, MSI_MESSAGE_ADDR_L32); | ||
305 | if (has_upper) { | ||
306 | msg_addr_upper = dw_pcie_readl_dbi(pci, MSI_MESSAGE_ADDR_U32); | ||
307 | msg_data = dw_pcie_readw_dbi(pci, MSI_MESSAGE_DATA_64); | ||
308 | } else { | ||
309 | msg_addr_upper = 0; | ||
310 | msg_data = dw_pcie_readw_dbi(pci, MSI_MESSAGE_DATA_32); | ||
311 | } | ||
312 | msg_addr = ((u64) msg_addr_upper) << 32 | msg_addr_lower; | ||
313 | ret = dw_pcie_ep_map_addr(epc, func_no, ep->msi_mem_phys, msg_addr, | ||
314 | epc->mem->page_size); | ||
315 | if (ret) | ||
316 | return ret; | ||
317 | |||
318 | writel(msg_data | (interrupt_num - 1), ep->msi_mem); | ||
319 | |||
320 | dw_pcie_ep_unmap_addr(epc, func_no, ep->msi_mem_phys); | ||
321 | |||
322 | return 0; | ||
323 | } | ||
324 | |||
325 | void dw_pcie_ep_exit(struct dw_pcie_ep *ep) | ||
326 | { | ||
327 | struct pci_epc *epc = ep->epc; | ||
328 | |||
329 | pci_epc_mem_free_addr(epc, ep->msi_mem_phys, ep->msi_mem, | ||
330 | epc->mem->page_size); | ||
331 | |||
332 | pci_epc_mem_exit(epc); | ||
333 | } | ||
334 | |||
335 | int dw_pcie_ep_init(struct dw_pcie_ep *ep) | ||
336 | { | ||
337 | int ret; | ||
338 | void *addr; | ||
339 | struct pci_epc *epc; | ||
340 | struct dw_pcie *pci = to_dw_pcie_from_ep(ep); | ||
341 | struct device *dev = pci->dev; | ||
342 | struct device_node *np = dev->of_node; | ||
343 | |||
344 | if (!pci->dbi_base || !pci->dbi_base2) { | ||
345 | dev_err(dev, "dbi_base/dbi_base2 is not populated\n"); | ||
346 | return -EINVAL; | ||
347 | } | ||
348 | |||
349 | ret = of_property_read_u32(np, "num-ib-windows", &ep->num_ib_windows); | ||
350 | if (ret < 0) { | ||
351 | dev_err(dev, "Unable to read *num-ib-windows* property\n"); | ||
352 | return ret; | ||
353 | } | ||
354 | if (ep->num_ib_windows > MAX_IATU_IN) { | ||
355 | dev_err(dev, "Invalid *num-ib-windows*\n"); | ||
356 | return -EINVAL; | ||
357 | } | ||
358 | |||
359 | ret = of_property_read_u32(np, "num-ob-windows", &ep->num_ob_windows); | ||
360 | if (ret < 0) { | ||
361 | dev_err(dev, "Unable to read *num-ob-windows* property\n"); | ||
362 | return ret; | ||
363 | } | ||
364 | if (ep->num_ob_windows > MAX_IATU_OUT) { | ||
365 | dev_err(dev, "Invalid *num-ob-windows*\n"); | ||
366 | return -EINVAL; | ||
367 | } | ||
368 | |||
369 | ep->ib_window_map = devm_kzalloc(dev, sizeof(long) * | ||
370 | BITS_TO_LONGS(ep->num_ib_windows), | ||
371 | GFP_KERNEL); | ||
372 | if (!ep->ib_window_map) | ||
373 | return -ENOMEM; | ||
374 | |||
375 | ep->ob_window_map = devm_kzalloc(dev, sizeof(long) * | ||
376 | BITS_TO_LONGS(ep->num_ob_windows), | ||
377 | GFP_KERNEL); | ||
378 | if (!ep->ob_window_map) | ||
379 | return -ENOMEM; | ||
380 | |||
381 | addr = devm_kzalloc(dev, sizeof(phys_addr_t) * ep->num_ob_windows, | ||
382 | GFP_KERNEL); | ||
383 | if (!addr) | ||
384 | return -ENOMEM; | ||
385 | ep->outbound_addr = addr; | ||
386 | |||
387 | if (ep->ops->ep_init) | ||
388 | ep->ops->ep_init(ep); | ||
389 | |||
390 | epc = devm_pci_epc_create(dev, &epc_ops); | ||
391 | if (IS_ERR(epc)) { | ||
392 | dev_err(dev, "Failed to create epc device\n"); | ||
393 | return PTR_ERR(epc); | ||
394 | } | ||
395 | |||
396 | ret = of_property_read_u8(np, "max-functions", &epc->max_functions); | ||
397 | if (ret < 0) | ||
398 | epc->max_functions = 1; | ||
399 | |||
400 | ret = __pci_epc_mem_init(epc, ep->phys_base, ep->addr_size, | ||
401 | ep->page_size); | ||
402 | if (ret < 0) { | ||
403 | dev_err(dev, "Failed to initialize address space\n"); | ||
404 | return ret; | ||
405 | } | ||
406 | |||
407 | ep->msi_mem = pci_epc_mem_alloc_addr(epc, &ep->msi_mem_phys, | ||
408 | epc->mem->page_size); | ||
409 | if (!ep->msi_mem) { | ||
410 | dev_err(dev, "Failed to reserve memory for MSI\n"); | ||
411 | return -ENOMEM; | ||
412 | } | ||
413 | |||
414 | epc->features = EPC_FEATURE_NO_LINKUP_NOTIFIER; | ||
415 | EPC_FEATURE_SET_BAR(epc->features, BAR_0); | ||
416 | |||
417 | ep->epc = epc; | ||
418 | epc_set_drvdata(epc, ep); | ||
419 | dw_pcie_setup(pci); | ||
420 | |||
421 | return 0; | ||
422 | } | ||