blob: 627c4b69878c673240b5b86750d971989dba971c [file] [log] [blame]
Bjorn Helgaas8cfab3c2018-01-26 12:50:27 -06001// SPDX-License-Identifier: GPL-2.0
Krzysztof Kozlowski9b41d192020-07-29 22:12:19 +02002/*
Bjorn Helgaas96291d52017-09-01 16:35:50 -05003 * Synopsys DesignWare PCIe Endpoint controller driver
Kishon Vijay Abraham If8aed6e2017-03-27 15:15:05 +05304 *
5 * Copyright (C) 2017 Texas Instruments
6 * Author: Kishon Vijay Abraham I <[email protected]>
Kishon Vijay Abraham If8aed6e2017-03-27 15:15:05 +05307 */
8
9#include <linux/of.h>
Rob Herringa0fd3612020-11-05 15:11:46 -060010#include <linux/platform_device.h>
Kishon Vijay Abraham If8aed6e2017-03-27 15:15:05 +053011
12#include "pcie-designware.h"
13#include <linux/pci-epc.h>
14#include <linux/pci-epf.h>
15
Rob Herring39bc5002020-08-20 21:54:14 -060016#include "../../pci.h"
17
Kishon Vijay Abraham If8aed6e2017-03-27 15:15:05 +053018void dw_pcie_ep_linkup(struct dw_pcie_ep *ep)
19{
20 struct pci_epc *epc = ep->epc;
21
22 pci_epc_linkup(epc);
23}
Vidya Sagarc57247f2020-03-03 23:40:52 +053024EXPORT_SYMBOL_GPL(dw_pcie_ep_linkup);
Kishon Vijay Abraham If8aed6e2017-03-27 15:15:05 +053025
Vidya Sagarac37dde2020-02-17 17:40:35 +053026void dw_pcie_ep_init_notify(struct dw_pcie_ep *ep)
27{
28 struct pci_epc *epc = ep->epc;
29
30 pci_epc_init_notify(epc);
31}
Vidya Sagarc57247f2020-03-03 23:40:52 +053032EXPORT_SYMBOL_GPL(dw_pcie_ep_init_notify);
Vidya Sagarac37dde2020-02-17 17:40:35 +053033
Xiaowei Bao47a06262020-09-18 16:00:16 +080034struct dw_pcie_ep_func *
35dw_pcie_ep_get_func_from_ep(struct dw_pcie_ep *ep, u8 func_no)
36{
37 struct dw_pcie_ep_func *ep_func;
38
39 list_for_each_entry(ep_func, &ep->func_list, list) {
40 if (ep_func->func_no == func_no)
41 return ep_func;
42 }
43
44 return NULL;
45}
46
Xiaowei Bao24ede432020-09-18 16:00:13 +080047static unsigned int dw_pcie_ep_func_select(struct dw_pcie_ep *ep, u8 func_no)
48{
49 unsigned int func_offset = 0;
50
51 if (ep->ops->func_conf_select)
52 func_offset = ep->ops->func_conf_select(ep, func_no);
53
54 return func_offset;
55}
56
57static void __dw_pcie_ep_reset_bar(struct dw_pcie *pci, u8 func_no,
58 enum pci_barno bar, int flags)
Kishon Vijay Abraham If8aed6e2017-03-27 15:15:05 +053059{
60 u32 reg;
Xiaowei Bao24ede432020-09-18 16:00:13 +080061 unsigned int func_offset = 0;
62 struct dw_pcie_ep *ep = &pci->ep;
Kishon Vijay Abraham If8aed6e2017-03-27 15:15:05 +053063
Xiaowei Bao24ede432020-09-18 16:00:13 +080064 func_offset = dw_pcie_ep_func_select(ep, func_no);
65
66 reg = func_offset + PCI_BASE_ADDRESS_0 + (4 * bar);
Niklas Cassel1cab8262017-12-20 00:29:24 +010067 dw_pcie_dbi_ro_wr_en(pci);
Kishon Vijay Abraham If8aed6e2017-03-27 15:15:05 +053068 dw_pcie_writel_dbi2(pci, reg, 0x0);
69 dw_pcie_writel_dbi(pci, reg, 0x0);
Niklas Cassel96a3be42018-03-28 13:50:16 +020070 if (flags & PCI_BASE_ADDRESS_MEM_TYPE_64) {
71 dw_pcie_writel_dbi2(pci, reg + 4, 0x0);
72 dw_pcie_writel_dbi(pci, reg + 4, 0x0);
73 }
Niklas Cassel1cab8262017-12-20 00:29:24 +010074 dw_pcie_dbi_ro_wr_dis(pci);
Kishon Vijay Abraham If8aed6e2017-03-27 15:15:05 +053075}
76
Niklas Cassel77d08db2018-03-28 13:50:14 +020077void dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar)
78{
Xiaowei Bao24ede432020-09-18 16:00:13 +080079 u8 func_no, funcs;
80
81 funcs = pci->ep.epc->max_functions;
82
83 for (func_no = 0; func_no < funcs; func_no++)
84 __dw_pcie_ep_reset_bar(pci, func_no, bar, 0);
Niklas Cassel77d08db2018-03-28 13:50:14 +020085}
Manivannan Sadhasivamf55fee52021-09-20 12:29:45 +053086EXPORT_SYMBOL_GPL(dw_pcie_ep_reset_bar);
Niklas Cassel77d08db2018-03-28 13:50:14 +020087
Xiaowei Bao47a06262020-09-18 16:00:16 +080088static u8 __dw_pcie_ep_find_next_cap(struct dw_pcie_ep *ep, u8 func_no,
89 u8 cap_ptr, u8 cap)
90{
91 struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
92 unsigned int func_offset = 0;
93 u8 cap_id, next_cap_ptr;
94 u16 reg;
95
96 if (!cap_ptr)
97 return 0;
98
99 func_offset = dw_pcie_ep_func_select(ep, func_no);
100
101 reg = dw_pcie_readw_dbi(pci, func_offset + cap_ptr);
102 cap_id = (reg & 0x00ff);
103
104 if (cap_id > PCI_CAP_ID_MAX)
105 return 0;
106
107 if (cap_id == cap)
108 return cap_ptr;
109
110 next_cap_ptr = (reg & 0xff00) >> 8;
111 return __dw_pcie_ep_find_next_cap(ep, func_no, next_cap_ptr, cap);
112}
113
114static u8 dw_pcie_ep_find_capability(struct dw_pcie_ep *ep, u8 func_no, u8 cap)
115{
116 struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
117 unsigned int func_offset = 0;
118 u8 next_cap_ptr;
119 u16 reg;
120
121 func_offset = dw_pcie_ep_func_select(ep, func_no);
122
123 reg = dw_pcie_readw_dbi(pci, func_offset + PCI_CAPABILITY_LIST);
124 next_cap_ptr = (reg & 0x00ff);
125
126 return __dw_pcie_ep_find_next_cap(ep, func_no, next_cap_ptr, cap);
127}
128
Kishon Vijay Abraham I53fd3cb2021-08-19 18:03:39 +0530129static int dw_pcie_ep_write_header(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
Kishon Vijay Abraham If8aed6e2017-03-27 15:15:05 +0530130 struct pci_epf_header *hdr)
131{
132 struct dw_pcie_ep *ep = epc_get_drvdata(epc);
133 struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
Xiaowei Bao24ede432020-09-18 16:00:13 +0800134 unsigned int func_offset = 0;
135
136 func_offset = dw_pcie_ep_func_select(ep, func_no);
Kishon Vijay Abraham If8aed6e2017-03-27 15:15:05 +0530137
Niklas Cassel1cab8262017-12-20 00:29:24 +0100138 dw_pcie_dbi_ro_wr_en(pci);
Xiaowei Bao24ede432020-09-18 16:00:13 +0800139 dw_pcie_writew_dbi(pci, func_offset + PCI_VENDOR_ID, hdr->vendorid);
140 dw_pcie_writew_dbi(pci, func_offset + PCI_DEVICE_ID, hdr->deviceid);
141 dw_pcie_writeb_dbi(pci, func_offset + PCI_REVISION_ID, hdr->revid);
142 dw_pcie_writeb_dbi(pci, func_offset + PCI_CLASS_PROG, hdr->progif_code);
143 dw_pcie_writew_dbi(pci, func_offset + PCI_CLASS_DEVICE,
Kishon Vijay Abraham If8aed6e2017-03-27 15:15:05 +0530144 hdr->subclass_code | hdr->baseclass_code << 8);
Xiaowei Bao24ede432020-09-18 16:00:13 +0800145 dw_pcie_writeb_dbi(pci, func_offset + PCI_CACHE_LINE_SIZE,
Kishon Vijay Abraham If8aed6e2017-03-27 15:15:05 +0530146 hdr->cache_line_size);
Xiaowei Bao24ede432020-09-18 16:00:13 +0800147 dw_pcie_writew_dbi(pci, func_offset + PCI_SUBSYSTEM_VENDOR_ID,
Kishon Vijay Abraham If8aed6e2017-03-27 15:15:05 +0530148 hdr->subsys_vendor_id);
Xiaowei Bao24ede432020-09-18 16:00:13 +0800149 dw_pcie_writew_dbi(pci, func_offset + PCI_SUBSYSTEM_ID, hdr->subsys_id);
150 dw_pcie_writeb_dbi(pci, func_offset + PCI_INTERRUPT_PIN,
Kishon Vijay Abraham If8aed6e2017-03-27 15:15:05 +0530151 hdr->interrupt_pin);
Niklas Cassel1cab8262017-12-20 00:29:24 +0100152 dw_pcie_dbi_ro_wr_dis(pci);
Kishon Vijay Abraham If8aed6e2017-03-27 15:15:05 +0530153
154 return 0;
155}
156
Serge Semin4859db92022-06-24 17:39:41 +0300157static int dw_pcie_ep_inbound_atu(struct dw_pcie_ep *ep, u8 func_no, int type,
158 dma_addr_t cpu_addr, enum pci_barno bar)
Kishon Vijay Abraham If8aed6e2017-03-27 15:15:05 +0530159{
160 int ret;
161 u32 free_win;
162 struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
163
Rob Herring9ca17af2020-11-05 15:11:58 -0600164 free_win = find_first_zero_bit(ep->ib_window_map, pci->num_ib_windows);
165 if (free_win >= pci->num_ib_windows) {
Gustavo Pimentelb4a8a512018-05-14 16:09:48 +0100166 dev_err(pci->dev, "No free inbound window\n");
Kishon Vijay Abraham If8aed6e2017-03-27 15:15:05 +0530167 return -EINVAL;
168 }
169
Serge Semin4859db92022-06-24 17:39:41 +0300170 ret = dw_pcie_prog_inbound_atu(pci, func_no, free_win, type,
171 cpu_addr, bar);
Kishon Vijay Abraham If8aed6e2017-03-27 15:15:05 +0530172 if (ret < 0) {
173 dev_err(pci->dev, "Failed to program IB window\n");
174 return ret;
175 }
176
177 ep->bar_to_atu[bar] = free_win;
Niklas Casselad4a5be2017-12-14 14:01:44 +0100178 set_bit(free_win, ep->ib_window_map);
Kishon Vijay Abraham If8aed6e2017-03-27 15:15:05 +0530179
180 return 0;
181}
182
Xiaowei Bao24ede432020-09-18 16:00:13 +0800183static int dw_pcie_ep_outbound_atu(struct dw_pcie_ep *ep, u8 func_no,
184 phys_addr_t phys_addr,
Kishon Vijay Abraham If8aed6e2017-03-27 15:15:05 +0530185 u64 pci_addr, size_t size)
186{
Kishon Vijay Abraham If8aed6e2017-03-27 15:15:05 +0530187 struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
Serge Semince06bf52022-06-24 17:39:46 +0300188 u32 free_win;
189 int ret;
Kishon Vijay Abraham If8aed6e2017-03-27 15:15:05 +0530190
Rob Herring9ca17af2020-11-05 15:11:58 -0600191 free_win = find_first_zero_bit(ep->ob_window_map, pci->num_ob_windows);
192 if (free_win >= pci->num_ob_windows) {
Gustavo Pimentelb4a8a512018-05-14 16:09:48 +0100193 dev_err(pci->dev, "No free outbound window\n");
Kishon Vijay Abraham If8aed6e2017-03-27 15:15:05 +0530194 return -EINVAL;
195 }
196
Serge Semince06bf52022-06-24 17:39:46 +0300197 ret = dw_pcie_prog_ep_outbound_atu(pci, func_no, free_win, PCIE_ATU_TYPE_MEM,
198 phys_addr, pci_addr, size);
199 if (ret)
200 return ret;
Kishon Vijay Abraham If8aed6e2017-03-27 15:15:05 +0530201
Niklas Casselad4a5be2017-12-14 14:01:44 +0100202 set_bit(free_win, ep->ob_window_map);
Kishon Vijay Abraham If8aed6e2017-03-27 15:15:05 +0530203 ep->outbound_addr[free_win] = phys_addr;
204
205 return 0;
206}
207
Kishon Vijay Abraham I53fd3cb2021-08-19 18:03:39 +0530208static void dw_pcie_ep_clear_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
Niklas Cassel77d08db2018-03-28 13:50:14 +0200209 struct pci_epf_bar *epf_bar)
Kishon Vijay Abraham If8aed6e2017-03-27 15:15:05 +0530210{
211 struct dw_pcie_ep *ep = epc_get_drvdata(epc);
212 struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
Niklas Cassel77d08db2018-03-28 13:50:14 +0200213 enum pci_barno bar = epf_bar->barno;
Kishon Vijay Abraham If8aed6e2017-03-27 15:15:05 +0530214 u32 atu_index = ep->bar_to_atu[bar];
215
Xiaowei Bao24ede432020-09-18 16:00:13 +0800216 __dw_pcie_ep_reset_bar(pci, func_no, bar, epf_bar->flags);
Kishon Vijay Abraham If8aed6e2017-03-27 15:15:05 +0530217
Serge Semin38fe2722022-06-24 17:39:42 +0300218 dw_pcie_disable_atu(pci, PCIE_ATU_REGION_DIR_IB, atu_index);
Niklas Casselad4a5be2017-12-14 14:01:44 +0100219 clear_bit(atu_index, ep->ib_window_map);
Kishon Vijay Abraham I6f5e1932020-02-25 13:47:02 +0530220 ep->epf_bar[bar] = NULL;
Kishon Vijay Abraham If8aed6e2017-03-27 15:15:05 +0530221}
222
Kishon Vijay Abraham I53fd3cb2021-08-19 18:03:39 +0530223static int dw_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
Niklas Casselbc4a4892018-03-28 13:50:07 +0200224 struct pci_epf_bar *epf_bar)
Kishon Vijay Abraham If8aed6e2017-03-27 15:15:05 +0530225{
Kishon Vijay Abraham If8aed6e2017-03-27 15:15:05 +0530226 struct dw_pcie_ep *ep = epc_get_drvdata(epc);
227 struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
Niklas Casselbc4a4892018-03-28 13:50:07 +0200228 enum pci_barno bar = epf_bar->barno;
229 size_t size = epf_bar->size;
230 int flags = epf_bar->flags;
Xiaowei Bao24ede432020-09-18 16:00:13 +0800231 unsigned int func_offset = 0;
Serge Semin4859db92022-06-24 17:39:41 +0300232 int ret, type;
233 u32 reg;
Xiaowei Bao24ede432020-09-18 16:00:13 +0800234
235 func_offset = dw_pcie_ep_func_select(ep, func_no);
236
237 reg = PCI_BASE_ADDRESS_0 + (4 * bar) + func_offset;
Kishon Vijay Abraham If8aed6e2017-03-27 15:15:05 +0530238
239 if (!(flags & PCI_BASE_ADDRESS_SPACE))
Serge Semin4859db92022-06-24 17:39:41 +0300240 type = PCIE_ATU_TYPE_MEM;
Kishon Vijay Abraham If8aed6e2017-03-27 15:15:05 +0530241 else
Serge Semin4859db92022-06-24 17:39:41 +0300242 type = PCIE_ATU_TYPE_IO;
Kishon Vijay Abraham If8aed6e2017-03-27 15:15:05 +0530243
Serge Semin4859db92022-06-24 17:39:41 +0300244 ret = dw_pcie_ep_inbound_atu(ep, func_no, type, epf_bar->phys_addr, bar);
Kishon Vijay Abraham If8aed6e2017-03-27 15:15:05 +0530245 if (ret)
246 return ret;
247
Niklas Cassel1cab8262017-12-20 00:29:24 +0100248 dw_pcie_dbi_ro_wr_en(pci);
Niklas Casseld28810b2018-03-28 13:50:11 +0200249
250 dw_pcie_writel_dbi2(pci, reg, lower_32_bits(size - 1));
Kishon Vijay Abraham If8aed6e2017-03-27 15:15:05 +0530251 dw_pcie_writel_dbi(pci, reg, flags);
Niklas Casseld28810b2018-03-28 13:50:11 +0200252
253 if (flags & PCI_BASE_ADDRESS_MEM_TYPE_64) {
254 dw_pcie_writel_dbi2(pci, reg + 4, upper_32_bits(size - 1));
255 dw_pcie_writel_dbi(pci, reg + 4, 0);
256 }
257
Kishon Vijay Abraham I6f5e1932020-02-25 13:47:02 +0530258 ep->epf_bar[bar] = epf_bar;
Niklas Cassel1cab8262017-12-20 00:29:24 +0100259 dw_pcie_dbi_ro_wr_dis(pci);
Kishon Vijay Abraham If8aed6e2017-03-27 15:15:05 +0530260
261 return 0;
262}
263
264static int dw_pcie_find_index(struct dw_pcie_ep *ep, phys_addr_t addr,
265 u32 *atu_index)
266{
267 u32 index;
Rob Herring9ca17af2020-11-05 15:11:58 -0600268 struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
Kishon Vijay Abraham If8aed6e2017-03-27 15:15:05 +0530269
Rob Herring9ca17af2020-11-05 15:11:58 -0600270 for (index = 0; index < pci->num_ob_windows; index++) {
Kishon Vijay Abraham If8aed6e2017-03-27 15:15:05 +0530271 if (ep->outbound_addr[index] != addr)
272 continue;
273 *atu_index = index;
274 return 0;
275 }
276
277 return -EINVAL;
278}
279
Kishon Vijay Abraham I53fd3cb2021-08-19 18:03:39 +0530280static void dw_pcie_ep_unmap_addr(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
Cyrille Pitchen44947382018-01-30 21:56:56 +0100281 phys_addr_t addr)
Kishon Vijay Abraham If8aed6e2017-03-27 15:15:05 +0530282{
283 int ret;
284 u32 atu_index;
285 struct dw_pcie_ep *ep = epc_get_drvdata(epc);
286 struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
287
288 ret = dw_pcie_find_index(ep, addr, &atu_index);
289 if (ret < 0)
290 return;
291
Serge Semin38fe2722022-06-24 17:39:42 +0300292 dw_pcie_disable_atu(pci, PCIE_ATU_REGION_DIR_OB, atu_index);
Niklas Casselad4a5be2017-12-14 14:01:44 +0100293 clear_bit(atu_index, ep->ob_window_map);
Kishon Vijay Abraham If8aed6e2017-03-27 15:15:05 +0530294}
295
Kishon Vijay Abraham I53fd3cb2021-08-19 18:03:39 +0530296static int dw_pcie_ep_map_addr(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
297 phys_addr_t addr, u64 pci_addr, size_t size)
Kishon Vijay Abraham If8aed6e2017-03-27 15:15:05 +0530298{
299 int ret;
300 struct dw_pcie_ep *ep = epc_get_drvdata(epc);
301 struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
302
Xiaowei Bao24ede432020-09-18 16:00:13 +0800303 ret = dw_pcie_ep_outbound_atu(ep, func_no, addr, pci_addr, size);
Kishon Vijay Abraham If8aed6e2017-03-27 15:15:05 +0530304 if (ret) {
Gustavo Pimentelb4a8a512018-05-14 16:09:48 +0100305 dev_err(pci->dev, "Failed to enable address\n");
Kishon Vijay Abraham If8aed6e2017-03-27 15:15:05 +0530306 return ret;
307 }
308
309 return 0;
310}
311
Kishon Vijay Abraham I53fd3cb2021-08-19 18:03:39 +0530312static int dw_pcie_ep_get_msi(struct pci_epc *epc, u8 func_no, u8 vfunc_no)
Kishon Vijay Abraham If8aed6e2017-03-27 15:15:05 +0530313{
Kishon Vijay Abraham If8aed6e2017-03-27 15:15:05 +0530314 struct dw_pcie_ep *ep = epc_get_drvdata(epc);
315 struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
Gustavo Pimentel3920a5d2018-07-19 10:32:15 +0200316 u32 val, reg;
Xiaowei Bao24ede432020-09-18 16:00:13 +0800317 unsigned int func_offset = 0;
Xiaowei Bao47a06262020-09-18 16:00:16 +0800318 struct dw_pcie_ep_func *ep_func;
Kishon Vijay Abraham If8aed6e2017-03-27 15:15:05 +0530319
Xiaowei Bao47a06262020-09-18 16:00:16 +0800320 ep_func = dw_pcie_ep_get_func_from_ep(ep, func_no);
321 if (!ep_func || !ep_func->msi_cap)
Kishon Vijay Abraham If8aed6e2017-03-27 15:15:05 +0530322 return -EINVAL;
323
Xiaowei Bao24ede432020-09-18 16:00:13 +0800324 func_offset = dw_pcie_ep_func_select(ep, func_no);
325
Xiaowei Bao47a06262020-09-18 16:00:16 +0800326 reg = ep_func->msi_cap + func_offset + PCI_MSI_FLAGS;
Gustavo Pimentel3920a5d2018-07-19 10:32:15 +0200327 val = dw_pcie_readw_dbi(pci, reg);
328 if (!(val & PCI_MSI_FLAGS_ENABLE))
329 return -EINVAL;
330
331 val = (val & PCI_MSI_FLAGS_QSIZE) >> 4;
332
Kishon Vijay Abraham If8aed6e2017-03-27 15:15:05 +0530333 return val;
334}
335
Kishon Vijay Abraham I53fd3cb2021-08-19 18:03:39 +0530336static int dw_pcie_ep_set_msi(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
337 u8 interrupts)
Kishon Vijay Abraham If8aed6e2017-03-27 15:15:05 +0530338{
Kishon Vijay Abraham If8aed6e2017-03-27 15:15:05 +0530339 struct dw_pcie_ep *ep = epc_get_drvdata(epc);
340 struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
Gustavo Pimentel3920a5d2018-07-19 10:32:15 +0200341 u32 val, reg;
Xiaowei Bao24ede432020-09-18 16:00:13 +0800342 unsigned int func_offset = 0;
Xiaowei Bao47a06262020-09-18 16:00:16 +0800343 struct dw_pcie_ep_func *ep_func;
Kishon Vijay Abraham If8aed6e2017-03-27 15:15:05 +0530344
Xiaowei Bao47a06262020-09-18 16:00:16 +0800345 ep_func = dw_pcie_ep_get_func_from_ep(ep, func_no);
346 if (!ep_func || !ep_func->msi_cap)
Gustavo Pimentel3920a5d2018-07-19 10:32:15 +0200347 return -EINVAL;
348
Xiaowei Bao24ede432020-09-18 16:00:13 +0800349 func_offset = dw_pcie_ep_func_select(ep, func_no);
350
Xiaowei Bao47a06262020-09-18 16:00:16 +0800351 reg = ep_func->msi_cap + func_offset + PCI_MSI_FLAGS;
Gustavo Pimentel3920a5d2018-07-19 10:32:15 +0200352 val = dw_pcie_readw_dbi(pci, reg);
353 val &= ~PCI_MSI_FLAGS_QMASK;
354 val |= (interrupts << 1) & PCI_MSI_FLAGS_QMASK;
Niklas Cassel1cab8262017-12-20 00:29:24 +0100355 dw_pcie_dbi_ro_wr_en(pci);
Gustavo Pimentel3920a5d2018-07-19 10:32:15 +0200356 dw_pcie_writew_dbi(pci, reg, val);
Niklas Cassel1cab8262017-12-20 00:29:24 +0100357 dw_pcie_dbi_ro_wr_dis(pci);
Kishon Vijay Abraham If8aed6e2017-03-27 15:15:05 +0530358
359 return 0;
360}
361
Kishon Vijay Abraham I53fd3cb2021-08-19 18:03:39 +0530362static int dw_pcie_ep_get_msix(struct pci_epc *epc, u8 func_no, u8 vfunc_no)
Gustavo Pimentelbeb46412018-07-19 10:32:14 +0200363{
364 struct dw_pcie_ep *ep = epc_get_drvdata(epc);
365 struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
366 u32 val, reg;
Xiaowei Bao24ede432020-09-18 16:00:13 +0800367 unsigned int func_offset = 0;
Xiaowei Bao47a06262020-09-18 16:00:16 +0800368 struct dw_pcie_ep_func *ep_func;
Gustavo Pimentelbeb46412018-07-19 10:32:14 +0200369
Xiaowei Bao47a06262020-09-18 16:00:16 +0800370 ep_func = dw_pcie_ep_get_func_from_ep(ep, func_no);
371 if (!ep_func || !ep_func->msix_cap)
Gustavo Pimentelbeb46412018-07-19 10:32:14 +0200372 return -EINVAL;
373
Xiaowei Bao24ede432020-09-18 16:00:13 +0800374 func_offset = dw_pcie_ep_func_select(ep, func_no);
375
Xiaowei Bao47a06262020-09-18 16:00:16 +0800376 reg = ep_func->msix_cap + func_offset + PCI_MSIX_FLAGS;
Gustavo Pimentelbeb46412018-07-19 10:32:14 +0200377 val = dw_pcie_readw_dbi(pci, reg);
378 if (!(val & PCI_MSIX_FLAGS_ENABLE))
379 return -EINVAL;
380
381 val &= PCI_MSIX_FLAGS_QSIZE;
382
383 return val;
384}
385
Kishon Vijay Abraham I53fd3cb2021-08-19 18:03:39 +0530386static int dw_pcie_ep_set_msix(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
387 u16 interrupts, enum pci_barno bir, u32 offset)
Gustavo Pimentelbeb46412018-07-19 10:32:14 +0200388{
389 struct dw_pcie_ep *ep = epc_get_drvdata(epc);
390 struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
391 u32 val, reg;
Xiaowei Bao24ede432020-09-18 16:00:13 +0800392 unsigned int func_offset = 0;
Xiaowei Bao47a06262020-09-18 16:00:16 +0800393 struct dw_pcie_ep_func *ep_func;
Gustavo Pimentelbeb46412018-07-19 10:32:14 +0200394
Xiaowei Bao47a06262020-09-18 16:00:16 +0800395 ep_func = dw_pcie_ep_get_func_from_ep(ep, func_no);
396 if (!ep_func || !ep_func->msix_cap)
Gustavo Pimentelbeb46412018-07-19 10:32:14 +0200397 return -EINVAL;
398
Kishon Vijay Abraham I83153d92020-02-25 13:47:01 +0530399 dw_pcie_dbi_ro_wr_en(pci);
400
Xiaowei Bao24ede432020-09-18 16:00:13 +0800401 func_offset = dw_pcie_ep_func_select(ep, func_no);
402
Xiaowei Bao47a06262020-09-18 16:00:16 +0800403 reg = ep_func->msix_cap + func_offset + PCI_MSIX_FLAGS;
Gustavo Pimentelbeb46412018-07-19 10:32:14 +0200404 val = dw_pcie_readw_dbi(pci, reg);
405 val &= ~PCI_MSIX_FLAGS_QSIZE;
406 val |= interrupts;
Gustavo Pimentelbeb46412018-07-19 10:32:14 +0200407 dw_pcie_writew_dbi(pci, reg, val);
Kishon Vijay Abraham I83153d92020-02-25 13:47:01 +0530408
Xiaowei Bao47a06262020-09-18 16:00:16 +0800409 reg = ep_func->msix_cap + func_offset + PCI_MSIX_TABLE;
Kishon Vijay Abraham I83153d92020-02-25 13:47:01 +0530410 val = offset | bir;
411 dw_pcie_writel_dbi(pci, reg, val);
412
Xiaowei Bao47a06262020-09-18 16:00:16 +0800413 reg = ep_func->msix_cap + func_offset + PCI_MSIX_PBA;
Kishon Vijay Abraham I83153d92020-02-25 13:47:01 +0530414 val = (offset + (interrupts * PCI_MSIX_ENTRY_SIZE)) | bir;
415 dw_pcie_writel_dbi(pci, reg, val);
416
Gustavo Pimentelbeb46412018-07-19 10:32:14 +0200417 dw_pcie_dbi_ro_wr_dis(pci);
418
419 return 0;
420}
421
Kishon Vijay Abraham I53fd3cb2021-08-19 18:03:39 +0530422static int dw_pcie_ep_raise_irq(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
Gustavo Pimenteld3c70a92018-07-19 10:32:13 +0200423 enum pci_epc_irq_type type, u16 interrupt_num)
Kishon Vijay Abraham If8aed6e2017-03-27 15:15:05 +0530424{
425 struct dw_pcie_ep *ep = epc_get_drvdata(epc);
426
427 if (!ep->ops->raise_irq)
428 return -EINVAL;
429
Bjorn Helgaas16093362018-02-01 11:36:07 -0600430 return ep->ops->raise_irq(ep, func_no, type, interrupt_num);
Kishon Vijay Abraham If8aed6e2017-03-27 15:15:05 +0530431}
432
433static void dw_pcie_ep_stop(struct pci_epc *epc)
434{
435 struct dw_pcie_ep *ep = epc_get_drvdata(epc);
436 struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
437
Serge Semina37beef2022-06-24 17:34:23 +0300438 dw_pcie_stop_link(pci);
Kishon Vijay Abraham If8aed6e2017-03-27 15:15:05 +0530439}
440
441static int dw_pcie_ep_start(struct pci_epc *epc)
442{
443 struct dw_pcie_ep *ep = epc_get_drvdata(epc);
444 struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
445
Serge Semina37beef2022-06-24 17:34:23 +0300446 return dw_pcie_start_link(pci);
Kishon Vijay Abraham If8aed6e2017-03-27 15:15:05 +0530447}
448
Kishon Vijay Abraham Ifee35cb2019-01-14 16:45:00 +0530449static const struct pci_epc_features*
Kishon Vijay Abraham I53fd3cb2021-08-19 18:03:39 +0530450dw_pcie_ep_get_features(struct pci_epc *epc, u8 func_no, u8 vfunc_no)
Kishon Vijay Abraham Ifee35cb2019-01-14 16:45:00 +0530451{
452 struct dw_pcie_ep *ep = epc_get_drvdata(epc);
453
454 if (!ep->ops->get_features)
455 return NULL;
456
457 return ep->ops->get_features(ep);
458}
459
Kishon Vijay Abraham If8aed6e2017-03-27 15:15:05 +0530460static const struct pci_epc_ops epc_ops = {
461 .write_header = dw_pcie_ep_write_header,
462 .set_bar = dw_pcie_ep_set_bar,
463 .clear_bar = dw_pcie_ep_clear_bar,
464 .map_addr = dw_pcie_ep_map_addr,
465 .unmap_addr = dw_pcie_ep_unmap_addr,
466 .set_msi = dw_pcie_ep_set_msi,
467 .get_msi = dw_pcie_ep_get_msi,
Gustavo Pimentelbeb46412018-07-19 10:32:14 +0200468 .set_msix = dw_pcie_ep_set_msix,
469 .get_msix = dw_pcie_ep_get_msix,
Kishon Vijay Abraham If8aed6e2017-03-27 15:15:05 +0530470 .raise_irq = dw_pcie_ep_raise_irq,
471 .start = dw_pcie_ep_start,
472 .stop = dw_pcie_ep_stop,
Kishon Vijay Abraham Ifee35cb2019-01-14 16:45:00 +0530473 .get_features = dw_pcie_ep_get_features,
Kishon Vijay Abraham If8aed6e2017-03-27 15:15:05 +0530474};
475
Gustavo Pimentelcb22d402018-07-19 10:32:16 +0200476int dw_pcie_ep_raise_legacy_irq(struct dw_pcie_ep *ep, u8 func_no)
477{
478 struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
479 struct device *dev = pci->dev;
480
481 dev_err(dev, "EP cannot trigger legacy IRQs\n");
482
483 return -EINVAL;
484}
Manivannan Sadhasivamf55fee52021-09-20 12:29:45 +0530485EXPORT_SYMBOL_GPL(dw_pcie_ep_raise_legacy_irq);
Gustavo Pimentelcb22d402018-07-19 10:32:16 +0200486
Bjorn Helgaas16093362018-02-01 11:36:07 -0600487int dw_pcie_ep_raise_msi_irq(struct dw_pcie_ep *ep, u8 func_no,
Niklas Cassel6f6d7872017-12-20 00:29:27 +0100488 u8 interrupt_num)
489{
490 struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
Xiaowei Bao47a06262020-09-18 16:00:16 +0800491 struct dw_pcie_ep_func *ep_func;
Niklas Cassel6f6d7872017-12-20 00:29:27 +0100492 struct pci_epc *epc = ep->epc;
Kishon Vijay Abraham I6b733032019-03-25 15:09:45 +0530493 unsigned int aligned_offset;
Xiaowei Bao24ede432020-09-18 16:00:13 +0800494 unsigned int func_offset = 0;
Niklas Cassel6f6d7872017-12-20 00:29:27 +0100495 u16 msg_ctrl, msg_data;
Gustavo Pimentel3920a5d2018-07-19 10:32:15 +0200496 u32 msg_addr_lower, msg_addr_upper, reg;
Niklas Cassel6f6d7872017-12-20 00:29:27 +0100497 u64 msg_addr;
498 bool has_upper;
499 int ret;
500
Xiaowei Bao47a06262020-09-18 16:00:16 +0800501 ep_func = dw_pcie_ep_get_func_from_ep(ep, func_no);
502 if (!ep_func || !ep_func->msi_cap)
Gustavo Pimentel3920a5d2018-07-19 10:32:15 +0200503 return -EINVAL;
504
Xiaowei Bao24ede432020-09-18 16:00:13 +0800505 func_offset = dw_pcie_ep_func_select(ep, func_no);
506
Niklas Cassel6f6d7872017-12-20 00:29:27 +0100507 /* Raise MSI per the PCI Local Bus Specification Revision 3.0, 6.8.1. */
Xiaowei Bao47a06262020-09-18 16:00:16 +0800508 reg = ep_func->msi_cap + func_offset + PCI_MSI_FLAGS;
Gustavo Pimentel3920a5d2018-07-19 10:32:15 +0200509 msg_ctrl = dw_pcie_readw_dbi(pci, reg);
Niklas Cassel6f6d7872017-12-20 00:29:27 +0100510 has_upper = !!(msg_ctrl & PCI_MSI_FLAGS_64BIT);
Xiaowei Bao47a06262020-09-18 16:00:16 +0800511 reg = ep_func->msi_cap + func_offset + PCI_MSI_ADDRESS_LO;
Gustavo Pimentel3920a5d2018-07-19 10:32:15 +0200512 msg_addr_lower = dw_pcie_readl_dbi(pci, reg);
Niklas Cassel6f6d7872017-12-20 00:29:27 +0100513 if (has_upper) {
Xiaowei Bao47a06262020-09-18 16:00:16 +0800514 reg = ep_func->msi_cap + func_offset + PCI_MSI_ADDRESS_HI;
Gustavo Pimentel3920a5d2018-07-19 10:32:15 +0200515 msg_addr_upper = dw_pcie_readl_dbi(pci, reg);
Xiaowei Bao47a06262020-09-18 16:00:16 +0800516 reg = ep_func->msi_cap + func_offset + PCI_MSI_DATA_64;
Gustavo Pimentel3920a5d2018-07-19 10:32:15 +0200517 msg_data = dw_pcie_readw_dbi(pci, reg);
Niklas Cassel6f6d7872017-12-20 00:29:27 +0100518 } else {
519 msg_addr_upper = 0;
Xiaowei Bao47a06262020-09-18 16:00:16 +0800520 reg = ep_func->msi_cap + func_offset + PCI_MSI_DATA_32;
Gustavo Pimentel3920a5d2018-07-19 10:32:15 +0200521 msg_data = dw_pcie_readw_dbi(pci, reg);
Niklas Cassel6f6d7872017-12-20 00:29:27 +0100522 }
Lad Prabhakard45e3c12020-05-07 13:33:16 +0100523 aligned_offset = msg_addr_lower & (epc->mem->window.page_size - 1);
Kishon Vijay Abraham I6b733032019-03-25 15:09:45 +0530524 msg_addr = ((u64)msg_addr_upper) << 32 |
525 (msg_addr_lower & ~aligned_offset);
Kishon Vijay Abraham I53fd3cb2021-08-19 18:03:39 +0530526 ret = dw_pcie_ep_map_addr(epc, func_no, 0, ep->msi_mem_phys, msg_addr,
Lad Prabhakard45e3c12020-05-07 13:33:16 +0100527 epc->mem->window.page_size);
Niklas Cassel6f6d7872017-12-20 00:29:27 +0100528 if (ret)
529 return ret;
530
Kishon Vijay Abraham I6b733032019-03-25 15:09:45 +0530531 writel(msg_data | (interrupt_num - 1), ep->msi_mem + aligned_offset);
Niklas Cassel6f6d7872017-12-20 00:29:27 +0100532
Kishon Vijay Abraham I53fd3cb2021-08-19 18:03:39 +0530533 dw_pcie_ep_unmap_addr(epc, func_no, 0, ep->msi_mem_phys);
Niklas Cassel6f6d7872017-12-20 00:29:27 +0100534
535 return 0;
536}
Manivannan Sadhasivamf55fee52021-09-20 12:29:45 +0530537EXPORT_SYMBOL_GPL(dw_pcie_ep_raise_msi_irq);
Niklas Cassel6f6d7872017-12-20 00:29:27 +0100538
Xiaowei Bao2f7f7002020-09-18 16:00:14 +0800539int dw_pcie_ep_raise_msix_irq_doorbell(struct dw_pcie_ep *ep, u8 func_no,
540 u16 interrupt_num)
541{
542 struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
543 struct dw_pcie_ep_func *ep_func;
544 u32 msg_data;
545
546 ep_func = dw_pcie_ep_get_func_from_ep(ep, func_no);
547 if (!ep_func || !ep_func->msix_cap)
548 return -EINVAL;
549
550 msg_data = (func_no << PCIE_MSIX_DOORBELL_PF_SHIFT) |
551 (interrupt_num - 1);
552
553 dw_pcie_writel_dbi(pci, PCIE_MSIX_DOORBELL, msg_data);
554
555 return 0;
556}
557
Gustavo Pimentelbeb46412018-07-19 10:32:14 +0200558int dw_pcie_ep_raise_msix_irq(struct dw_pcie_ep *ep, u8 func_no,
Xiaowei Bao24ede432020-09-18 16:00:13 +0800559 u16 interrupt_num)
Gustavo Pimentelbeb46412018-07-19 10:32:14 +0200560{
561 struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
Xiaowei Bao47a06262020-09-18 16:00:16 +0800562 struct dw_pcie_ep_func *ep_func;
Kishon Vijay Abraham I6f5e1932020-02-25 13:47:02 +0530563 struct pci_epf_msix_tbl *msix_tbl;
Gustavo Pimentelbeb46412018-07-19 10:32:14 +0200564 struct pci_epc *epc = ep->epc;
Xiaowei Bao24ede432020-09-18 16:00:13 +0800565 unsigned int func_offset = 0;
Gustavo Pimentelbeb46412018-07-19 10:32:14 +0200566 u32 reg, msg_data, vec_ctrl;
Kishon Vijay Abraham I6f5e1932020-02-25 13:47:02 +0530567 unsigned int aligned_offset;
568 u32 tbl_offset;
569 u64 msg_addr;
Gustavo Pimentelbeb46412018-07-19 10:32:14 +0200570 int ret;
Kishon Vijay Abraham I6f5e1932020-02-25 13:47:02 +0530571 u8 bir;
Gustavo Pimentelbeb46412018-07-19 10:32:14 +0200572
Xiaowei Bao47a06262020-09-18 16:00:16 +0800573 ep_func = dw_pcie_ep_get_func_from_ep(ep, func_no);
574 if (!ep_func || !ep_func->msix_cap)
575 return -EINVAL;
576
Xiaowei Bao24ede432020-09-18 16:00:13 +0800577 func_offset = dw_pcie_ep_func_select(ep, func_no);
578
Xiaowei Bao47a06262020-09-18 16:00:16 +0800579 reg = ep_func->msix_cap + func_offset + PCI_MSIX_TABLE;
Gustavo Pimentelbeb46412018-07-19 10:32:14 +0200580 tbl_offset = dw_pcie_readl_dbi(pci, reg);
581 bir = (tbl_offset & PCI_MSIX_TABLE_BIR);
582 tbl_offset &= PCI_MSIX_TABLE_OFFSET;
Gustavo Pimentelbeb46412018-07-19 10:32:14 +0200583
Jiri Slabybf711622020-04-20 08:52:27 +0200584 msix_tbl = ep->epf_bar[bir]->addr + tbl_offset;
Kishon Vijay Abraham I6f5e1932020-02-25 13:47:02 +0530585 msg_addr = msix_tbl[(interrupt_num - 1)].msg_addr;
586 msg_data = msix_tbl[(interrupt_num - 1)].msg_data;
587 vec_ctrl = msix_tbl[(interrupt_num - 1)].vector_ctrl;
Gustavo Pimentelbeb46412018-07-19 10:32:14 +0200588
Gustavo Pimentel0380cf82018-12-07 18:24:37 +0100589 if (vec_ctrl & PCI_MSIX_ENTRY_CTRL_MASKBIT) {
590 dev_dbg(pci->dev, "MSI-X entry ctrl set\n");
Gustavo Pimentelbeb46412018-07-19 10:32:14 +0200591 return -EPERM;
Gustavo Pimentel0380cf82018-12-07 18:24:37 +0100592 }
Gustavo Pimentelbeb46412018-07-19 10:32:14 +0200593
Lad Prabhakard45e3c12020-05-07 13:33:16 +0100594 aligned_offset = msg_addr & (epc->mem->window.page_size - 1);
Kishon Vijay Abraham I53fd3cb2021-08-19 18:03:39 +0530595 ret = dw_pcie_ep_map_addr(epc, func_no, 0, ep->msi_mem_phys, msg_addr,
Lad Prabhakard45e3c12020-05-07 13:33:16 +0100596 epc->mem->window.page_size);
Gustavo Pimentelbeb46412018-07-19 10:32:14 +0200597 if (ret)
598 return ret;
599
Kishon Vijay Abraham I6f5e1932020-02-25 13:47:02 +0530600 writel(msg_data, ep->msi_mem + aligned_offset);
Gustavo Pimentelbeb46412018-07-19 10:32:14 +0200601
Kishon Vijay Abraham I53fd3cb2021-08-19 18:03:39 +0530602 dw_pcie_ep_unmap_addr(epc, func_no, 0, ep->msi_mem_phys);
Gustavo Pimentelbeb46412018-07-19 10:32:14 +0200603
604 return 0;
605}
606
Kishon Vijay Abraham If8aed6e2017-03-27 15:15:05 +0530607void dw_pcie_ep_exit(struct dw_pcie_ep *ep)
608{
609 struct pci_epc *epc = ep->epc;
610
Niklas Cassel2fd0c9d2017-12-20 00:29:25 +0100611 pci_epc_mem_free_addr(epc, ep->msi_mem_phys, ep->msi_mem,
Lad Prabhakard45e3c12020-05-07 13:33:16 +0100612 epc->mem->window.page_size);
Niklas Cassel2fd0c9d2017-12-20 00:29:25 +0100613
Kishon Vijay Abraham If8aed6e2017-03-27 15:15:05 +0530614 pci_epc_mem_exit(epc);
615}
616
Kishon Vijay Abraham Ifc9a7702019-03-25 15:09:44 +0530617static unsigned int dw_pcie_ep_find_ext_capability(struct dw_pcie *pci, int cap)
618{
619 u32 header;
620 int pos = PCI_CFG_SPACE_SIZE;
621
622 while (pos) {
623 header = dw_pcie_readl_dbi(pci, pos);
624 if (PCI_EXT_CAP_ID(header) == cap)
625 return pos;
626
627 pos = PCI_EXT_CAP_NEXT(header);
628 if (!pos)
629 break;
630 }
631
632 return 0;
633}
634
Vidya Sagare966f732020-02-17 17:40:33 +0530635int dw_pcie_ep_init_complete(struct dw_pcie_ep *ep)
636{
637 struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
638 unsigned int offset;
639 unsigned int nbars;
640 u8 hdr_type;
641 u32 reg;
642 int i;
643
Hou Zhiqiang16270a92020-08-18 17:27:46 +0800644 hdr_type = dw_pcie_readb_dbi(pci, PCI_HEADER_TYPE) &
645 PCI_HEADER_TYPE_MASK;
Vidya Sagare966f732020-02-17 17:40:33 +0530646 if (hdr_type != PCI_HEADER_TYPE_NORMAL) {
647 dev_err(pci->dev,
648 "PCIe controller is not set to EP mode (hdr_type:0x%x)!\n",
649 hdr_type);
650 return -EIO;
651 }
652
Vidya Sagare966f732020-02-17 17:40:33 +0530653 offset = dw_pcie_ep_find_ext_capability(pci, PCI_EXT_CAP_ID_REBAR);
Rob Herring39bc5002020-08-20 21:54:14 -0600654
655 dw_pcie_dbi_ro_wr_en(pci);
656
Vidya Sagare966f732020-02-17 17:40:33 +0530657 if (offset) {
658 reg = dw_pcie_readl_dbi(pci, offset + PCI_REBAR_CTRL);
659 nbars = (reg & PCI_REBAR_CTRL_NBAR_MASK) >>
660 PCI_REBAR_CTRL_NBAR_SHIFT;
661
Vidya Sagare966f732020-02-17 17:40:33 +0530662 for (i = 0; i < nbars; i++, offset += PCI_REBAR_CTRL)
663 dw_pcie_writel_dbi(pci, offset + PCI_REBAR_CAP, 0x0);
Vidya Sagare966f732020-02-17 17:40:33 +0530664 }
665
666 dw_pcie_setup(pci);
Rob Herring39bc5002020-08-20 21:54:14 -0600667 dw_pcie_dbi_ro_wr_dis(pci);
Vidya Sagare966f732020-02-17 17:40:33 +0530668
669 return 0;
670}
Vidya Sagarc57247f2020-03-03 23:40:52 +0530671EXPORT_SYMBOL_GPL(dw_pcie_ep_init_complete);
Vidya Sagare966f732020-02-17 17:40:33 +0530672
Kishon Vijay Abraham If8aed6e2017-03-27 15:15:05 +0530673int dw_pcie_ep_init(struct dw_pcie_ep *ep)
674{
675 int ret;
676 void *addr;
Xiaowei Bao47a06262020-09-18 16:00:16 +0800677 u8 func_no;
Rob Herringa0fd3612020-11-05 15:11:46 -0600678 struct resource *res;
Kishon Vijay Abraham If8aed6e2017-03-27 15:15:05 +0530679 struct pci_epc *epc;
680 struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
681 struct device *dev = pci->dev;
Rob Herringa0fd3612020-11-05 15:11:46 -0600682 struct platform_device *pdev = to_platform_device(dev);
Kishon Vijay Abraham If8aed6e2017-03-27 15:15:05 +0530683 struct device_node *np = dev->of_node;
Vidya Sagare966f732020-02-17 17:40:33 +0530684 const struct pci_epc_features *epc_features;
Xiaowei Bao47a06262020-09-18 16:00:16 +0800685 struct dw_pcie_ep_func *ep_func;
686
687 INIT_LIST_HEAD(&ep->func_list);
Kishon Vijay Abraham If8aed6e2017-03-27 15:15:05 +0530688
Rob Herringa0fd3612020-11-05 15:11:46 -0600689 if (!pci->dbi_base) {
690 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi");
691 pci->dbi_base = devm_pci_remap_cfg_resource(dev, res);
692 if (IS_ERR(pci->dbi_base))
693 return PTR_ERR(pci->dbi_base);
Kishon Vijay Abraham If8aed6e2017-03-27 15:15:05 +0530694 }
695
Rob Herringa0fd3612020-11-05 15:11:46 -0600696 if (!pci->dbi_base2) {
697 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi2");
Serge Semin816f5052022-06-24 17:34:17 +0300698 if (!res) {
Rob Herringa0fd3612020-11-05 15:11:46 -0600699 pci->dbi_base2 = pci->dbi_base + SZ_4K;
Serge Semin816f5052022-06-24 17:34:17 +0300700 } else {
Rob Herringa0fd3612020-11-05 15:11:46 -0600701 pci->dbi_base2 = devm_pci_remap_cfg_resource(dev, res);
702 if (IS_ERR(pci->dbi_base2))
703 return PTR_ERR(pci->dbi_base2);
704 }
705 }
706
707 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "addr_space");
708 if (!res)
709 return -EINVAL;
710
711 ep->phys_base = res->start;
712 ep->addr_size = resource_size(res);
713
Serge Semin13e9d392022-06-24 17:39:36 +0300714 dw_pcie_version_detect(pci);
715
Serge Semine3dc79a2022-06-24 17:39:34 +0300716 dw_pcie_iatu_detect(pci);
717
Kees Cooka86854d2018-06-12 14:07:58 -0700718 ep->ib_window_map = devm_kcalloc(dev,
Rob Herring9ca17af2020-11-05 15:11:58 -0600719 BITS_TO_LONGS(pci->num_ib_windows),
Kees Cooka86854d2018-06-12 14:07:58 -0700720 sizeof(long),
Niklas Casselad4a5be2017-12-14 14:01:44 +0100721 GFP_KERNEL);
722 if (!ep->ib_window_map)
723 return -ENOMEM;
724
Kees Cooka86854d2018-06-12 14:07:58 -0700725 ep->ob_window_map = devm_kcalloc(dev,
Rob Herring9ca17af2020-11-05 15:11:58 -0600726 BITS_TO_LONGS(pci->num_ob_windows),
Kees Cooka86854d2018-06-12 14:07:58 -0700727 sizeof(long),
Niklas Casselad4a5be2017-12-14 14:01:44 +0100728 GFP_KERNEL);
729 if (!ep->ob_window_map)
730 return -ENOMEM;
Kishon Vijay Abraham If8aed6e2017-03-27 15:15:05 +0530731
Rob Herring9ca17af2020-11-05 15:11:58 -0600732 addr = devm_kcalloc(dev, pci->num_ob_windows, sizeof(phys_addr_t),
Kishon Vijay Abraham If8aed6e2017-03-27 15:15:05 +0530733 GFP_KERNEL);
734 if (!addr)
735 return -ENOMEM;
736 ep->outbound_addr = addr;
737
Rob Herring39bc5002020-08-20 21:54:14 -0600738 if (pci->link_gen < 1)
739 pci->link_gen = of_pci_get_max_link_speed(np);
740
Kishon Vijay Abraham If8aed6e2017-03-27 15:15:05 +0530741 epc = devm_pci_epc_create(dev, &epc_ops);
742 if (IS_ERR(epc)) {
Gustavo Pimentelb4a8a512018-05-14 16:09:48 +0100743 dev_err(dev, "Failed to create epc device\n");
Kishon Vijay Abraham If8aed6e2017-03-27 15:15:05 +0530744 return PTR_ERR(epc);
745 }
746
Gustavo Pimentel4e965ed2018-07-19 10:32:11 +0200747 ep->epc = epc;
748 epc_set_drvdata(epc, ep);
749
Kishon Vijay Abraham If8aed6e2017-03-27 15:15:05 +0530750 ret = of_property_read_u8(np, "max-functions", &epc->max_functions);
751 if (ret < 0)
752 epc->max_functions = 1;
753
Xiaowei Bao47a06262020-09-18 16:00:16 +0800754 for (func_no = 0; func_no < epc->max_functions; func_no++) {
755 ep_func = devm_kzalloc(dev, sizeof(*ep_func), GFP_KERNEL);
756 if (!ep_func)
757 return -ENOMEM;
Xiaowei Bao6bfc9c32020-09-18 16:00:15 +0800758
Xiaowei Bao47a06262020-09-18 16:00:16 +0800759 ep_func->func_no = func_no;
760 ep_func->msi_cap = dw_pcie_ep_find_capability(ep, func_no,
761 PCI_CAP_ID_MSI);
762 ep_func->msix_cap = dw_pcie_ep_find_capability(ep, func_no,
763 PCI_CAP_ID_MSIX);
764
765 list_add_tail(&ep_func->list, &ep->func_list);
766 }
Xiaowei Bao6bfc9c32020-09-18 16:00:15 +0800767
Xiaowei Bao24ede432020-09-18 16:00:13 +0800768 if (ep->ops->ep_init)
769 ep->ops->ep_init(ep);
770
Lad Prabhakard45e3c12020-05-07 13:33:16 +0100771 ret = pci_epc_mem_init(epc, ep->phys_base, ep->addr_size,
772 ep->page_size);
Kishon Vijay Abraham If8aed6e2017-03-27 15:15:05 +0530773 if (ret < 0) {
774 dev_err(dev, "Failed to initialize address space\n");
775 return ret;
776 }
777
Niklas Cassel2fd0c9d2017-12-20 00:29:25 +0100778 ep->msi_mem = pci_epc_mem_alloc_addr(epc, &ep->msi_mem_phys,
Lad Prabhakard45e3c12020-05-07 13:33:16 +0100779 epc->mem->window.page_size);
Niklas Cassel2fd0c9d2017-12-20 00:29:25 +0100780 if (!ep->msi_mem) {
Serge Semin8161e962022-06-24 17:34:15 +0300781 ret = -ENOMEM;
Gustavo Pimentelbeb46412018-07-19 10:32:14 +0200782 dev_err(dev, "Failed to reserve memory for MSI/MSI-X\n");
Serge Semin8161e962022-06-24 17:34:15 +0300783 goto err_exit_epc_mem;
Niklas Cassel2fd0c9d2017-12-20 00:29:25 +0100784 }
Gustavo Pimentelbeb46412018-07-19 10:32:14 +0200785
Vidya Sagare966f732020-02-17 17:40:33 +0530786 if (ep->ops->get_features) {
787 epc_features = ep->ops->get_features(ep);
788 if (epc_features->core_init_notifier)
789 return 0;
Kishon Vijay Abraham Ifc9a7702019-03-25 15:09:44 +0530790 }
791
Serge Semin8161e962022-06-24 17:34:15 +0300792 ret = dw_pcie_ep_init_complete(ep);
793 if (ret)
794 goto err_free_epc_mem;
795
796 return 0;
797
798err_free_epc_mem:
799 pci_epc_mem_free_addr(epc, ep->msi_mem_phys, ep->msi_mem,
800 epc->mem->window.page_size);
801
802err_exit_epc_mem:
803 pci_epc_mem_exit(epc);
804
805 return ret;
Kishon Vijay Abraham If8aed6e2017-03-27 15:15:05 +0530806}
Vidya Sagarc57247f2020-03-03 23:40:52 +0530807EXPORT_SYMBOL_GPL(dw_pcie_ep_init);