Skip to content

Commit f8aed6e

Browse files
kishonbjorn-helgaas
authored andcommitted
PCI: dwc: designware: Add EP mode support
Add endpoint mode support to designware driver. This uses the EP Core layer introduced recently to add endpoint mode support. *Any* function driver can now use this designware device in order to achieve the EP functionality. Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com> Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
1 parent 1357053 commit f8aed6e

File tree

5 files changed

+578
-0
lines changed

5 files changed

+578
-0
lines changed

drivers/pci/dwc/Kconfig

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ config PCIE_DW_HOST
99
depends on PCI_MSI_IRQ_DOMAIN
1010
select PCIE_DW
1111

12+
config PCIE_DW_EP
13+
bool
14+
depends on PCI_ENDPOINT
15+
select PCIE_DW
16+
1217
config PCI_DRA7XX
1318
bool "TI DRA7xx PCIe controller"
1419
depends on PCI

drivers/pci/dwc/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
obj-$(CONFIG_PCIE_DW) += pcie-designware.o
22
obj-$(CONFIG_PCIE_DW_HOST) += pcie-designware-host.o
3+
obj-$(CONFIG_PCIE_DW_EP) += pcie-designware-ep.o
34
obj-$(CONFIG_PCIE_DW_PLAT) += pcie-designware-plat.o
45
obj-$(CONFIG_PCI_DRA7XX) += pci-dra7xx.o
56
obj-$(CONFIG_PCI_EXYNOS) += pci-exynos.o

drivers/pci/dwc/pcie-designware-ep.c

Lines changed: 342 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,342 @@
1+
/**
2+
* Synopsys Designware PCIe Endpoint controller driver
3+
*
4+
* Copyright (C) 2017 Texas Instruments
5+
* Author: Kishon Vijay Abraham I <kishon@ti.com>
6+
*
7+
* This program is free software: you can redistribute it and/or modify
8+
* it under the terms of the GNU General Public License version 2 of
9+
* the License as published by the Free Software Foundation.
10+
*
11+
* This program is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* GNU General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU General Public License
17+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
18+
*/
19+
20+
#include <linux/of.h>
21+
22+
#include "pcie-designware.h"
23+
#include <linux/pci-epc.h>
24+
#include <linux/pci-epf.h>
25+
26+
void dw_pcie_ep_linkup(struct dw_pcie_ep *ep)
27+
{
28+
struct pci_epc *epc = ep->epc;
29+
30+
pci_epc_linkup(epc);
31+
}
32+
33+
static void dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar)
34+
{
35+
u32 reg;
36+
37+
reg = PCI_BASE_ADDRESS_0 + (4 * bar);
38+
dw_pcie_writel_dbi2(pci, reg, 0x0);
39+
dw_pcie_writel_dbi(pci, reg, 0x0);
40+
}
41+
42+
static int dw_pcie_ep_write_header(struct pci_epc *epc,
43+
struct pci_epf_header *hdr)
44+
{
45+
struct dw_pcie_ep *ep = epc_get_drvdata(epc);
46+
struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
47+
48+
dw_pcie_writew_dbi(pci, PCI_VENDOR_ID, hdr->vendorid);
49+
dw_pcie_writew_dbi(pci, PCI_DEVICE_ID, hdr->deviceid);
50+
dw_pcie_writeb_dbi(pci, PCI_REVISION_ID, hdr->revid);
51+
dw_pcie_writeb_dbi(pci, PCI_CLASS_PROG, hdr->progif_code);
52+
dw_pcie_writew_dbi(pci, PCI_CLASS_DEVICE,
53+
hdr->subclass_code | hdr->baseclass_code << 8);
54+
dw_pcie_writeb_dbi(pci, PCI_CACHE_LINE_SIZE,
55+
hdr->cache_line_size);
56+
dw_pcie_writew_dbi(pci, PCI_SUBSYSTEM_VENDOR_ID,
57+
hdr->subsys_vendor_id);
58+
dw_pcie_writew_dbi(pci, PCI_SUBSYSTEM_ID, hdr->subsys_id);
59+
dw_pcie_writeb_dbi(pci, PCI_INTERRUPT_PIN,
60+
hdr->interrupt_pin);
61+
62+
return 0;
63+
}
64+
65+
static int dw_pcie_ep_inbound_atu(struct dw_pcie_ep *ep, enum pci_barno bar,
66+
dma_addr_t cpu_addr,
67+
enum dw_pcie_as_type as_type)
68+
{
69+
int ret;
70+
u32 free_win;
71+
struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
72+
73+
free_win = find_first_zero_bit(&ep->ib_window_map,
74+
sizeof(ep->ib_window_map));
75+
if (free_win >= ep->num_ib_windows) {
76+
dev_err(pci->dev, "no free inbound window\n");
77+
return -EINVAL;
78+
}
79+
80+
ret = dw_pcie_prog_inbound_atu(pci, free_win, bar, cpu_addr,
81+
as_type);
82+
if (ret < 0) {
83+
dev_err(pci->dev, "Failed to program IB window\n");
84+
return ret;
85+
}
86+
87+
ep->bar_to_atu[bar] = free_win;
88+
set_bit(free_win, &ep->ib_window_map);
89+
90+
return 0;
91+
}
92+
93+
static int dw_pcie_ep_outbound_atu(struct dw_pcie_ep *ep, phys_addr_t phys_addr,
94+
u64 pci_addr, size_t size)
95+
{
96+
u32 free_win;
97+
struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
98+
99+
free_win = find_first_zero_bit(&ep->ob_window_map,
100+
sizeof(ep->ob_window_map));
101+
if (free_win >= ep->num_ob_windows) {
102+
dev_err(pci->dev, "no free outbound window\n");
103+
return -EINVAL;
104+
}
105+
106+
dw_pcie_prog_outbound_atu(pci, free_win, PCIE_ATU_TYPE_MEM,
107+
phys_addr, pci_addr, size);
108+
109+
set_bit(free_win, &ep->ob_window_map);
110+
ep->outbound_addr[free_win] = phys_addr;
111+
112+
return 0;
113+
}
114+
115+
static void dw_pcie_ep_clear_bar(struct pci_epc *epc, enum pci_barno bar)
116+
{
117+
struct dw_pcie_ep *ep = epc_get_drvdata(epc);
118+
struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
119+
u32 atu_index = ep->bar_to_atu[bar];
120+
121+
dw_pcie_ep_reset_bar(pci, bar);
122+
123+
dw_pcie_disable_atu(pci, atu_index, DW_PCIE_REGION_INBOUND);
124+
clear_bit(atu_index, &ep->ib_window_map);
125+
}
126+
127+
static int dw_pcie_ep_set_bar(struct pci_epc *epc, enum pci_barno bar,
128+
dma_addr_t bar_phys, size_t size, int flags)
129+
{
130+
int ret;
131+
struct dw_pcie_ep *ep = epc_get_drvdata(epc);
132+
struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
133+
enum dw_pcie_as_type as_type;
134+
u32 reg = PCI_BASE_ADDRESS_0 + (4 * bar);
135+
136+
if (!(flags & PCI_BASE_ADDRESS_SPACE))
137+
as_type = DW_PCIE_AS_MEM;
138+
else
139+
as_type = DW_PCIE_AS_IO;
140+
141+
ret = dw_pcie_ep_inbound_atu(ep, bar, bar_phys, as_type);
142+
if (ret)
143+
return ret;
144+
145+
dw_pcie_writel_dbi2(pci, reg, size - 1);
146+
dw_pcie_writel_dbi(pci, reg, flags);
147+
148+
return 0;
149+
}
150+
151+
static int dw_pcie_find_index(struct dw_pcie_ep *ep, phys_addr_t addr,
152+
u32 *atu_index)
153+
{
154+
u32 index;
155+
156+
for (index = 0; index < ep->num_ob_windows; index++) {
157+
if (ep->outbound_addr[index] != addr)
158+
continue;
159+
*atu_index = index;
160+
return 0;
161+
}
162+
163+
return -EINVAL;
164+
}
165+
166+
static void dw_pcie_ep_unmap_addr(struct pci_epc *epc, phys_addr_t addr)
167+
{
168+
int ret;
169+
u32 atu_index;
170+
struct dw_pcie_ep *ep = epc_get_drvdata(epc);
171+
struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
172+
173+
ret = dw_pcie_find_index(ep, addr, &atu_index);
174+
if (ret < 0)
175+
return;
176+
177+
dw_pcie_disable_atu(pci, atu_index, DW_PCIE_REGION_OUTBOUND);
178+
clear_bit(atu_index, &ep->ob_window_map);
179+
}
180+
181+
static int dw_pcie_ep_map_addr(struct pci_epc *epc, phys_addr_t addr,
182+
u64 pci_addr, size_t size)
183+
{
184+
int ret;
185+
struct dw_pcie_ep *ep = epc_get_drvdata(epc);
186+
struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
187+
188+
ret = dw_pcie_ep_outbound_atu(ep, addr, pci_addr, size);
189+
if (ret) {
190+
dev_err(pci->dev, "failed to enable address\n");
191+
return ret;
192+
}
193+
194+
return 0;
195+
}
196+
197+
static int dw_pcie_ep_get_msi(struct pci_epc *epc)
198+
{
199+
int val;
200+
u32 lower_addr;
201+
u32 upper_addr;
202+
struct dw_pcie_ep *ep = epc_get_drvdata(epc);
203+
struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
204+
205+
val = dw_pcie_readb_dbi(pci, MSI_MESSAGE_CONTROL);
206+
val = (val & MSI_CAP_MME_MASK) >> MSI_CAP_MME_SHIFT;
207+
208+
lower_addr = dw_pcie_readl_dbi(pci, MSI_MESSAGE_ADDR_L32);
209+
upper_addr = dw_pcie_readl_dbi(pci, MSI_MESSAGE_ADDR_U32);
210+
211+
if (!(lower_addr || upper_addr))
212+
return -EINVAL;
213+
214+
return val;
215+
}
216+
217+
static int dw_pcie_ep_set_msi(struct pci_epc *epc, u8 encode_int)
218+
{
219+
int val;
220+
struct dw_pcie_ep *ep = epc_get_drvdata(epc);
221+
struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
222+
223+
val = (encode_int << MSI_CAP_MMC_SHIFT);
224+
dw_pcie_writew_dbi(pci, MSI_MESSAGE_CONTROL, val);
225+
226+
return 0;
227+
}
228+
229+
static int dw_pcie_ep_raise_irq(struct pci_epc *epc,
230+
enum pci_epc_irq_type type, u8 interrupt_num)
231+
{
232+
struct dw_pcie_ep *ep = epc_get_drvdata(epc);
233+
234+
if (!ep->ops->raise_irq)
235+
return -EINVAL;
236+
237+
return ep->ops->raise_irq(ep, type, interrupt_num);
238+
}
239+
240+
static void dw_pcie_ep_stop(struct pci_epc *epc)
241+
{
242+
struct dw_pcie_ep *ep = epc_get_drvdata(epc);
243+
struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
244+
245+
if (!pci->ops->stop_link)
246+
return;
247+
248+
pci->ops->stop_link(pci);
249+
}
250+
251+
static int dw_pcie_ep_start(struct pci_epc *epc)
252+
{
253+
struct dw_pcie_ep *ep = epc_get_drvdata(epc);
254+
struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
255+
256+
if (!pci->ops->start_link)
257+
return -EINVAL;
258+
259+
return pci->ops->start_link(pci);
260+
}
261+
262+
static const struct pci_epc_ops epc_ops = {
263+
.write_header = dw_pcie_ep_write_header,
264+
.set_bar = dw_pcie_ep_set_bar,
265+
.clear_bar = dw_pcie_ep_clear_bar,
266+
.map_addr = dw_pcie_ep_map_addr,
267+
.unmap_addr = dw_pcie_ep_unmap_addr,
268+
.set_msi = dw_pcie_ep_set_msi,
269+
.get_msi = dw_pcie_ep_get_msi,
270+
.raise_irq = dw_pcie_ep_raise_irq,
271+
.start = dw_pcie_ep_start,
272+
.stop = dw_pcie_ep_stop,
273+
};
274+
275+
void dw_pcie_ep_exit(struct dw_pcie_ep *ep)
276+
{
277+
struct pci_epc *epc = ep->epc;
278+
279+
pci_epc_mem_exit(epc);
280+
}
281+
282+
int dw_pcie_ep_init(struct dw_pcie_ep *ep)
283+
{
284+
int ret;
285+
void *addr;
286+
enum pci_barno bar;
287+
struct pci_epc *epc;
288+
struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
289+
struct device *dev = pci->dev;
290+
struct device_node *np = dev->of_node;
291+
292+
if (!pci->dbi_base || !pci->dbi_base2) {
293+
dev_err(dev, "dbi_base/deb_base2 is not populated\n");
294+
return -EINVAL;
295+
}
296+
297+
ret = of_property_read_u32(np, "num-ib-windows", &ep->num_ib_windows);
298+
if (ret < 0) {
299+
dev_err(dev, "unable to read *num-ib-windows* property\n");
300+
return ret;
301+
}
302+
303+
ret = of_property_read_u32(np, "num-ob-windows", &ep->num_ob_windows);
304+
if (ret < 0) {
305+
dev_err(dev, "unable to read *num-ob-windows* property\n");
306+
return ret;
307+
}
308+
309+
addr = devm_kzalloc(dev, sizeof(phys_addr_t) * ep->num_ob_windows,
310+
GFP_KERNEL);
311+
if (!addr)
312+
return -ENOMEM;
313+
ep->outbound_addr = addr;
314+
315+
for (bar = BAR_0; bar <= BAR_5; bar++)
316+
dw_pcie_ep_reset_bar(pci, bar);
317+
318+
if (ep->ops->ep_init)
319+
ep->ops->ep_init(ep);
320+
321+
epc = devm_pci_epc_create(dev, &epc_ops);
322+
if (IS_ERR(epc)) {
323+
dev_err(dev, "failed to create epc device\n");
324+
return PTR_ERR(epc);
325+
}
326+
327+
ret = of_property_read_u8(np, "max-functions", &epc->max_functions);
328+
if (ret < 0)
329+
epc->max_functions = 1;
330+
331+
ret = pci_epc_mem_init(epc, ep->phys_base, ep->addr_size);
332+
if (ret < 0) {
333+
dev_err(dev, "Failed to initialize address space\n");
334+
return ret;
335+
}
336+
337+
ep->epc = epc;
338+
epc_set_drvdata(epc, ep);
339+
dw_pcie_setup(pci);
340+
341+
return 0;
342+
}

0 commit comments

Comments
 (0)