16e0832faSShawn Lin // SPDX-License-Identifier: GPL-2.0
29b41d19aSKrzysztof Kozlowski /*
36e0832faSShawn Lin * Synopsys DesignWare PCIe Endpoint controller driver
46e0832faSShawn Lin *
56e0832faSShawn Lin * Copyright (C) 2017 Texas Instruments
66e0832faSShawn Lin * Author: Kishon Vijay Abraham I <kishon@ti.com>
76e0832faSShawn Lin */
86e0832faSShawn Lin
96ea2f3b9SDan Carpenter #include <linux/align.h>
106e0832faSShawn Lin #include <linux/of.h>
11a0fd361dSRob Herring #include <linux/platform_device.h>
126e0832faSShawn Lin
136e0832faSShawn Lin #include "pcie-designware.h"
146e0832faSShawn Lin #include <linux/pci-epc.h>
156e0832faSShawn Lin #include <linux/pci-epf.h>
166e0832faSShawn Lin
dw_pcie_ep_linkup(struct dw_pcie_ep * ep)176e0832faSShawn Lin void dw_pcie_ep_linkup(struct dw_pcie_ep *ep)
186e0832faSShawn Lin {
196e0832faSShawn Lin struct pci_epc *epc = ep->epc;
206e0832faSShawn Lin
216e0832faSShawn Lin pci_epc_linkup(epc);
226e0832faSShawn Lin }
23c57247f9SVidya Sagar EXPORT_SYMBOL_GPL(dw_pcie_ep_linkup);
246e0832faSShawn Lin
dw_pcie_ep_init_notify(struct dw_pcie_ep * ep)25ac37dde7SVidya Sagar void dw_pcie_ep_init_notify(struct dw_pcie_ep *ep)
26ac37dde7SVidya Sagar {
27ac37dde7SVidya Sagar struct pci_epc *epc = ep->epc;
28ac37dde7SVidya Sagar
29ac37dde7SVidya Sagar pci_epc_init_notify(epc);
30ac37dde7SVidya Sagar }
31c57247f9SVidya Sagar EXPORT_SYMBOL_GPL(dw_pcie_ep_init_notify);
32ac37dde7SVidya Sagar
3347a06260SXiaowei Bao struct dw_pcie_ep_func *
dw_pcie_ep_get_func_from_ep(struct dw_pcie_ep * ep,u8 func_no)3447a06260SXiaowei Bao dw_pcie_ep_get_func_from_ep(struct dw_pcie_ep *ep, u8 func_no)
3547a06260SXiaowei Bao {
3647a06260SXiaowei Bao struct dw_pcie_ep_func *ep_func;
3747a06260SXiaowei Bao
3847a06260SXiaowei Bao list_for_each_entry(ep_func, &ep->func_list, list) {
3947a06260SXiaowei Bao if (ep_func->func_no == func_no)
4047a06260SXiaowei Bao return ep_func;
4147a06260SXiaowei Bao }
4247a06260SXiaowei Bao
4347a06260SXiaowei Bao return NULL;
4447a06260SXiaowei Bao }
4547a06260SXiaowei Bao
dw_pcie_ep_func_select(struct dw_pcie_ep * ep,u8 func_no)4624ede430SXiaowei Bao static unsigned int dw_pcie_ep_func_select(struct dw_pcie_ep *ep, u8 func_no)
4724ede430SXiaowei Bao {
4824ede430SXiaowei Bao unsigned int func_offset = 0;
4924ede430SXiaowei Bao
5024ede430SXiaowei Bao if (ep->ops->func_conf_select)
5124ede430SXiaowei Bao func_offset = ep->ops->func_conf_select(ep, func_no);
5224ede430SXiaowei Bao
5324ede430SXiaowei Bao return func_offset;
5424ede430SXiaowei Bao }
5524ede430SXiaowei Bao
__dw_pcie_ep_reset_bar(struct dw_pcie * pci,u8 func_no,enum pci_barno bar,int flags)5624ede430SXiaowei Bao static void __dw_pcie_ep_reset_bar(struct dw_pcie *pci, u8 func_no,
5724ede430SXiaowei Bao enum pci_barno bar, int flags)
586e0832faSShawn Lin {
596e0832faSShawn Lin u32 reg;
6024ede430SXiaowei Bao unsigned int func_offset = 0;
6124ede430SXiaowei Bao struct dw_pcie_ep *ep = &pci->ep;
626e0832faSShawn Lin
6324ede430SXiaowei Bao func_offset = dw_pcie_ep_func_select(ep, func_no);
6424ede430SXiaowei Bao
6524ede430SXiaowei Bao reg = func_offset + PCI_BASE_ADDRESS_0 + (4 * bar);
666e0832faSShawn Lin dw_pcie_dbi_ro_wr_en(pci);
676e0832faSShawn Lin dw_pcie_writel_dbi2(pci, reg, 0x0);
686e0832faSShawn Lin dw_pcie_writel_dbi(pci, reg, 0x0);
696e0832faSShawn Lin if (flags & PCI_BASE_ADDRESS_MEM_TYPE_64) {
706e0832faSShawn Lin dw_pcie_writel_dbi2(pci, reg + 4, 0x0);
716e0832faSShawn Lin dw_pcie_writel_dbi(pci, reg + 4, 0x0);
726e0832faSShawn Lin }
736e0832faSShawn Lin dw_pcie_dbi_ro_wr_dis(pci);
746e0832faSShawn Lin }
756e0832faSShawn Lin
dw_pcie_ep_reset_bar(struct dw_pcie * pci,enum pci_barno bar)766e0832faSShawn Lin void dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar)
776e0832faSShawn Lin {
7824ede430SXiaowei Bao u8 func_no, funcs;
7924ede430SXiaowei Bao
8024ede430SXiaowei Bao funcs = pci->ep.epc->max_functions;
8124ede430SXiaowei Bao
8224ede430SXiaowei Bao for (func_no = 0; func_no < funcs; func_no++)
8324ede430SXiaowei Bao __dw_pcie_ep_reset_bar(pci, func_no, bar, 0);
846e0832faSShawn Lin }
85f55fee56SManivannan Sadhasivam EXPORT_SYMBOL_GPL(dw_pcie_ep_reset_bar);
866e0832faSShawn Lin
__dw_pcie_ep_find_next_cap(struct dw_pcie_ep * ep,u8 func_no,u8 cap_ptr,u8 cap)8747a06260SXiaowei Bao static u8 __dw_pcie_ep_find_next_cap(struct dw_pcie_ep *ep, u8 func_no,
8847a06260SXiaowei Bao u8 cap_ptr, u8 cap)
8947a06260SXiaowei Bao {
9047a06260SXiaowei Bao struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
9147a06260SXiaowei Bao unsigned int func_offset = 0;
9247a06260SXiaowei Bao u8 cap_id, next_cap_ptr;
9347a06260SXiaowei Bao u16 reg;
9447a06260SXiaowei Bao
9547a06260SXiaowei Bao if (!cap_ptr)
9647a06260SXiaowei Bao return 0;
9747a06260SXiaowei Bao
9847a06260SXiaowei Bao func_offset = dw_pcie_ep_func_select(ep, func_no);
9947a06260SXiaowei Bao
10047a06260SXiaowei Bao reg = dw_pcie_readw_dbi(pci, func_offset + cap_ptr);
10147a06260SXiaowei Bao cap_id = (reg & 0x00ff);
10247a06260SXiaowei Bao
10347a06260SXiaowei Bao if (cap_id > PCI_CAP_ID_MAX)
10447a06260SXiaowei Bao return 0;
10547a06260SXiaowei Bao
10647a06260SXiaowei Bao if (cap_id == cap)
10747a06260SXiaowei Bao return cap_ptr;
10847a06260SXiaowei Bao
10947a06260SXiaowei Bao next_cap_ptr = (reg & 0xff00) >> 8;
11047a06260SXiaowei Bao return __dw_pcie_ep_find_next_cap(ep, func_no, next_cap_ptr, cap);
11147a06260SXiaowei Bao }
11247a06260SXiaowei Bao
dw_pcie_ep_find_capability(struct dw_pcie_ep * ep,u8 func_no,u8 cap)11347a06260SXiaowei Bao static u8 dw_pcie_ep_find_capability(struct dw_pcie_ep *ep, u8 func_no, u8 cap)
11447a06260SXiaowei Bao {
11547a06260SXiaowei Bao struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
11647a06260SXiaowei Bao unsigned int func_offset = 0;
11747a06260SXiaowei Bao u8 next_cap_ptr;
11847a06260SXiaowei Bao u16 reg;
11947a06260SXiaowei Bao
12047a06260SXiaowei Bao func_offset = dw_pcie_ep_func_select(ep, func_no);
12147a06260SXiaowei Bao
12247a06260SXiaowei Bao reg = dw_pcie_readw_dbi(pci, func_offset + PCI_CAPABILITY_LIST);
12347a06260SXiaowei Bao next_cap_ptr = (reg & 0x00ff);
12447a06260SXiaowei Bao
12547a06260SXiaowei Bao return __dw_pcie_ep_find_next_cap(ep, func_no, next_cap_ptr, cap);
12647a06260SXiaowei Bao }
12747a06260SXiaowei Bao
dw_pcie_ep_write_header(struct pci_epc * epc,u8 func_no,u8 vfunc_no,struct pci_epf_header * hdr)12853fd3cbeSKishon Vijay Abraham I static int dw_pcie_ep_write_header(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
1296e0832faSShawn Lin struct pci_epf_header *hdr)
1306e0832faSShawn Lin {
1316e0832faSShawn Lin struct dw_pcie_ep *ep = epc_get_drvdata(epc);
1326e0832faSShawn Lin struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
13324ede430SXiaowei Bao unsigned int func_offset = 0;
13424ede430SXiaowei Bao
13524ede430SXiaowei Bao func_offset = dw_pcie_ep_func_select(ep, func_no);
1366e0832faSShawn Lin
1376e0832faSShawn Lin dw_pcie_dbi_ro_wr_en(pci);
13824ede430SXiaowei Bao dw_pcie_writew_dbi(pci, func_offset + PCI_VENDOR_ID, hdr->vendorid);
13924ede430SXiaowei Bao dw_pcie_writew_dbi(pci, func_offset + PCI_DEVICE_ID, hdr->deviceid);
14024ede430SXiaowei Bao dw_pcie_writeb_dbi(pci, func_offset + PCI_REVISION_ID, hdr->revid);
14124ede430SXiaowei Bao dw_pcie_writeb_dbi(pci, func_offset + PCI_CLASS_PROG, hdr->progif_code);
14224ede430SXiaowei Bao dw_pcie_writew_dbi(pci, func_offset + PCI_CLASS_DEVICE,
1436e0832faSShawn Lin hdr->subclass_code | hdr->baseclass_code << 8);
14424ede430SXiaowei Bao dw_pcie_writeb_dbi(pci, func_offset + PCI_CACHE_LINE_SIZE,
1456e0832faSShawn Lin hdr->cache_line_size);
14624ede430SXiaowei Bao dw_pcie_writew_dbi(pci, func_offset + PCI_SUBSYSTEM_VENDOR_ID,
1476e0832faSShawn Lin hdr->subsys_vendor_id);
14824ede430SXiaowei Bao dw_pcie_writew_dbi(pci, func_offset + PCI_SUBSYSTEM_ID, hdr->subsys_id);
14924ede430SXiaowei Bao dw_pcie_writeb_dbi(pci, func_offset + PCI_INTERRUPT_PIN,
1506e0832faSShawn Lin hdr->interrupt_pin);
1516e0832faSShawn Lin dw_pcie_dbi_ro_wr_dis(pci);
1526e0832faSShawn Lin
1536e0832faSShawn Lin return 0;
1546e0832faSShawn Lin }
1556e0832faSShawn Lin
dw_pcie_ep_inbound_atu(struct dw_pcie_ep * ep,u8 func_no,int type,dma_addr_t cpu_addr,enum pci_barno bar)1564859db9bSSerge Semin static int dw_pcie_ep_inbound_atu(struct dw_pcie_ep *ep, u8 func_no, int type,
1574859db9bSSerge Semin dma_addr_t cpu_addr, enum pci_barno bar)
1586e0832faSShawn Lin {
1596e0832faSShawn Lin int ret;
1606e0832faSShawn Lin u32 free_win;
1616e0832faSShawn Lin struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
1626e0832faSShawn Lin
1634284c88fSFrank Li if (!ep->bar_to_atu[bar])
1649ca17af5SRob Herring free_win = find_first_zero_bit(ep->ib_window_map, pci->num_ib_windows);
1654284c88fSFrank Li else
166*92446499SFrank Li free_win = ep->bar_to_atu[bar] - 1;
1674284c88fSFrank Li
1689ca17af5SRob Herring if (free_win >= pci->num_ib_windows) {
1696e0832faSShawn Lin dev_err(pci->dev, "No free inbound window\n");
1706e0832faSShawn Lin return -EINVAL;
1716e0832faSShawn Lin }
1726e0832faSShawn Lin
1738522e17dSSerge Semin ret = dw_pcie_prog_ep_inbound_atu(pci, func_no, free_win, type,
1744859db9bSSerge Semin cpu_addr, bar);
1756e0832faSShawn Lin if (ret < 0) {
1766e0832faSShawn Lin dev_err(pci->dev, "Failed to program IB window\n");
1776e0832faSShawn Lin return ret;
1786e0832faSShawn Lin }
1796e0832faSShawn Lin
180*92446499SFrank Li /*
181*92446499SFrank Li * Always increment free_win before assignment, since value 0 is used to identify
182*92446499SFrank Li * unallocated mapping.
183*92446499SFrank Li */
184*92446499SFrank Li ep->bar_to_atu[bar] = free_win + 1;
1856e0832faSShawn Lin set_bit(free_win, ep->ib_window_map);
1866e0832faSShawn Lin
1876e0832faSShawn Lin return 0;
1886e0832faSShawn Lin }
1896e0832faSShawn Lin
dw_pcie_ep_outbound_atu(struct dw_pcie_ep * ep,u8 func_no,phys_addr_t phys_addr,u64 pci_addr,size_t size)19024ede430SXiaowei Bao static int dw_pcie_ep_outbound_atu(struct dw_pcie_ep *ep, u8 func_no,
19124ede430SXiaowei Bao phys_addr_t phys_addr,
1926e0832faSShawn Lin u64 pci_addr, size_t size)
1936e0832faSShawn Lin {
1946e0832faSShawn Lin struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
195ce06bf57SSerge Semin u32 free_win;
196ce06bf57SSerge Semin int ret;
1976e0832faSShawn Lin
1989ca17af5SRob Herring free_win = find_first_zero_bit(ep->ob_window_map, pci->num_ob_windows);
1999ca17af5SRob Herring if (free_win >= pci->num_ob_windows) {
2006e0832faSShawn Lin dev_err(pci->dev, "No free outbound window\n");
2016e0832faSShawn Lin return -EINVAL;
2026e0832faSShawn Lin }
2036e0832faSShawn Lin
204ce06bf57SSerge Semin ret = dw_pcie_prog_ep_outbound_atu(pci, func_no, free_win, PCIE_ATU_TYPE_MEM,
2056e0832faSShawn Lin phys_addr, pci_addr, size);
206ce06bf57SSerge Semin if (ret)
207ce06bf57SSerge Semin return ret;
2086e0832faSShawn Lin
2096e0832faSShawn Lin set_bit(free_win, ep->ob_window_map);
2106e0832faSShawn Lin ep->outbound_addr[free_win] = phys_addr;
2116e0832faSShawn Lin
2126e0832faSShawn Lin return 0;
2136e0832faSShawn Lin }
2146e0832faSShawn Lin
dw_pcie_ep_clear_bar(struct pci_epc * epc,u8 func_no,u8 vfunc_no,struct pci_epf_bar * epf_bar)21553fd3cbeSKishon Vijay Abraham I static void dw_pcie_ep_clear_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
2166e0832faSShawn Lin struct pci_epf_bar *epf_bar)
2176e0832faSShawn Lin {
2186e0832faSShawn Lin struct dw_pcie_ep *ep = epc_get_drvdata(epc);
2196e0832faSShawn Lin struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
2206e0832faSShawn Lin enum pci_barno bar = epf_bar->barno;
221*92446499SFrank Li u32 atu_index = ep->bar_to_atu[bar] - 1;
222*92446499SFrank Li
223*92446499SFrank Li if (!ep->bar_to_atu[bar])
224*92446499SFrank Li return;
2256e0832faSShawn Lin
22624ede430SXiaowei Bao __dw_pcie_ep_reset_bar(pci, func_no, bar, epf_bar->flags);
2276e0832faSShawn Lin
22838fe2723SSerge Semin dw_pcie_disable_atu(pci, PCIE_ATU_REGION_DIR_IB, atu_index);
2296e0832faSShawn Lin clear_bit(atu_index, ep->ib_window_map);
2306f5e193bSKishon Vijay Abraham I ep->epf_bar[bar] = NULL;
2314284c88fSFrank Li ep->bar_to_atu[bar] = 0;
2326e0832faSShawn Lin }
2336e0832faSShawn Lin
dw_pcie_ep_set_bar(struct pci_epc * epc,u8 func_no,u8 vfunc_no,struct pci_epf_bar * epf_bar)23453fd3cbeSKishon Vijay Abraham I static int dw_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
2356e0832faSShawn Lin struct pci_epf_bar *epf_bar)
2366e0832faSShawn Lin {
2376e0832faSShawn Lin struct dw_pcie_ep *ep = epc_get_drvdata(epc);
2386e0832faSShawn Lin struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
2396e0832faSShawn Lin enum pci_barno bar = epf_bar->barno;
2406e0832faSShawn Lin size_t size = epf_bar->size;
2416e0832faSShawn Lin int flags = epf_bar->flags;
24224ede430SXiaowei Bao unsigned int func_offset = 0;
2434859db9bSSerge Semin int ret, type;
2444859db9bSSerge Semin u32 reg;
24524ede430SXiaowei Bao
24624ede430SXiaowei Bao func_offset = dw_pcie_ep_func_select(ep, func_no);
24724ede430SXiaowei Bao
24824ede430SXiaowei Bao reg = PCI_BASE_ADDRESS_0 + (4 * bar) + func_offset;
2496e0832faSShawn Lin
2506e0832faSShawn Lin if (!(flags & PCI_BASE_ADDRESS_SPACE))
2514859db9bSSerge Semin type = PCIE_ATU_TYPE_MEM;
2526e0832faSShawn Lin else
2534859db9bSSerge Semin type = PCIE_ATU_TYPE_IO;
2546e0832faSShawn Lin
2554859db9bSSerge Semin ret = dw_pcie_ep_inbound_atu(ep, func_no, type, epf_bar->phys_addr, bar);
2566e0832faSShawn Lin if (ret)
2576e0832faSShawn Lin return ret;
2586e0832faSShawn Lin
2594284c88fSFrank Li if (ep->epf_bar[bar])
2604284c88fSFrank Li return 0;
2614284c88fSFrank Li
2626e0832faSShawn Lin dw_pcie_dbi_ro_wr_en(pci);
2636e0832faSShawn Lin
2646e0832faSShawn Lin dw_pcie_writel_dbi2(pci, reg, lower_32_bits(size - 1));
2656e0832faSShawn Lin dw_pcie_writel_dbi(pci, reg, flags);
2666e0832faSShawn Lin
2676e0832faSShawn Lin if (flags & PCI_BASE_ADDRESS_MEM_TYPE_64) {
2686e0832faSShawn Lin dw_pcie_writel_dbi2(pci, reg + 4, upper_32_bits(size - 1));
2696e0832faSShawn Lin dw_pcie_writel_dbi(pci, reg + 4, 0);
2706e0832faSShawn Lin }
2716e0832faSShawn Lin
2726f5e193bSKishon Vijay Abraham I ep->epf_bar[bar] = epf_bar;
2736e0832faSShawn Lin dw_pcie_dbi_ro_wr_dis(pci);
2746e0832faSShawn Lin
2756e0832faSShawn Lin return 0;
2766e0832faSShawn Lin }
2776e0832faSShawn Lin
dw_pcie_find_index(struct dw_pcie_ep * ep,phys_addr_t addr,u32 * atu_index)2786e0832faSShawn Lin static int dw_pcie_find_index(struct dw_pcie_ep *ep, phys_addr_t addr,
2796e0832faSShawn Lin u32 *atu_index)
2806e0832faSShawn Lin {
2816e0832faSShawn Lin u32 index;
2829ca17af5SRob Herring struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
2836e0832faSShawn Lin
2849ca17af5SRob Herring for (index = 0; index < pci->num_ob_windows; index++) {
2856e0832faSShawn Lin if (ep->outbound_addr[index] != addr)
2866e0832faSShawn Lin continue;
2876e0832faSShawn Lin *atu_index = index;
2886e0832faSShawn Lin return 0;
2896e0832faSShawn Lin }
2906e0832faSShawn Lin
2916e0832faSShawn Lin return -EINVAL;
2926e0832faSShawn Lin }
2936e0832faSShawn Lin
dw_pcie_ep_unmap_addr(struct pci_epc * epc,u8 func_no,u8 vfunc_no,phys_addr_t addr)29453fd3cbeSKishon Vijay Abraham I static void dw_pcie_ep_unmap_addr(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
2956e0832faSShawn Lin phys_addr_t addr)
2966e0832faSShawn Lin {
2976e0832faSShawn Lin int ret;
2986e0832faSShawn Lin u32 atu_index;
2996e0832faSShawn Lin struct dw_pcie_ep *ep = epc_get_drvdata(epc);
3006e0832faSShawn Lin struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
3016e0832faSShawn Lin
3026e0832faSShawn Lin ret = dw_pcie_find_index(ep, addr, &atu_index);
3036e0832faSShawn Lin if (ret < 0)
3046e0832faSShawn Lin return;
3056e0832faSShawn Lin
30638fe2723SSerge Semin dw_pcie_disable_atu(pci, PCIE_ATU_REGION_DIR_OB, atu_index);
3076e0832faSShawn Lin clear_bit(atu_index, ep->ob_window_map);
3086e0832faSShawn Lin }
3096e0832faSShawn Lin
dw_pcie_ep_map_addr(struct pci_epc * epc,u8 func_no,u8 vfunc_no,phys_addr_t addr,u64 pci_addr,size_t size)31053fd3cbeSKishon Vijay Abraham I static int dw_pcie_ep_map_addr(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
31153fd3cbeSKishon Vijay Abraham I phys_addr_t addr, u64 pci_addr, size_t size)
3126e0832faSShawn Lin {
3136e0832faSShawn Lin int ret;
3146e0832faSShawn Lin struct dw_pcie_ep *ep = epc_get_drvdata(epc);
3156e0832faSShawn Lin struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
3166e0832faSShawn Lin
31724ede430SXiaowei Bao ret = dw_pcie_ep_outbound_atu(ep, func_no, addr, pci_addr, size);
3186e0832faSShawn Lin if (ret) {
3196e0832faSShawn Lin dev_err(pci->dev, "Failed to enable address\n");
3206e0832faSShawn Lin return ret;
3216e0832faSShawn Lin }
3226e0832faSShawn Lin
3236e0832faSShawn Lin return 0;
3246e0832faSShawn Lin }
3256e0832faSShawn Lin
dw_pcie_ep_get_msi(struct pci_epc * epc,u8 func_no,u8 vfunc_no)32653fd3cbeSKishon Vijay Abraham I static int dw_pcie_ep_get_msi(struct pci_epc *epc, u8 func_no, u8 vfunc_no)
3276e0832faSShawn Lin {
3286e0832faSShawn Lin struct dw_pcie_ep *ep = epc_get_drvdata(epc);
3296e0832faSShawn Lin struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
3303920a5d7SGustavo Pimentel u32 val, reg;
33124ede430SXiaowei Bao unsigned int func_offset = 0;
33247a06260SXiaowei Bao struct dw_pcie_ep_func *ep_func;
3336e0832faSShawn Lin
33447a06260SXiaowei Bao ep_func = dw_pcie_ep_get_func_from_ep(ep, func_no);
33547a06260SXiaowei Bao if (!ep_func || !ep_func->msi_cap)
3366e0832faSShawn Lin return -EINVAL;
3376e0832faSShawn Lin
33824ede430SXiaowei Bao func_offset = dw_pcie_ep_func_select(ep, func_no);
33924ede430SXiaowei Bao
34047a06260SXiaowei Bao reg = ep_func->msi_cap + func_offset + PCI_MSI_FLAGS;
3413920a5d7SGustavo Pimentel val = dw_pcie_readw_dbi(pci, reg);
3423920a5d7SGustavo Pimentel if (!(val & PCI_MSI_FLAGS_ENABLE))
3433920a5d7SGustavo Pimentel return -EINVAL;
3443920a5d7SGustavo Pimentel
3453920a5d7SGustavo Pimentel val = (val & PCI_MSI_FLAGS_QSIZE) >> 4;
3463920a5d7SGustavo Pimentel
3476e0832faSShawn Lin return val;
3486e0832faSShawn Lin }
3496e0832faSShawn Lin
dw_pcie_ep_set_msi(struct pci_epc * epc,u8 func_no,u8 vfunc_no,u8 interrupts)35053fd3cbeSKishon Vijay Abraham I static int dw_pcie_ep_set_msi(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
35153fd3cbeSKishon Vijay Abraham I u8 interrupts)
3526e0832faSShawn Lin {
3536e0832faSShawn Lin struct dw_pcie_ep *ep = epc_get_drvdata(epc);
3546e0832faSShawn Lin struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
3553920a5d7SGustavo Pimentel u32 val, reg;
35624ede430SXiaowei Bao unsigned int func_offset = 0;
35747a06260SXiaowei Bao struct dw_pcie_ep_func *ep_func;
3586e0832faSShawn Lin
35947a06260SXiaowei Bao ep_func = dw_pcie_ep_get_func_from_ep(ep, func_no);
36047a06260SXiaowei Bao if (!ep_func || !ep_func->msi_cap)
3613920a5d7SGustavo Pimentel return -EINVAL;
3623920a5d7SGustavo Pimentel
36324ede430SXiaowei Bao func_offset = dw_pcie_ep_func_select(ep, func_no);
36424ede430SXiaowei Bao
36547a06260SXiaowei Bao reg = ep_func->msi_cap + func_offset + PCI_MSI_FLAGS;
3663920a5d7SGustavo Pimentel val = dw_pcie_readw_dbi(pci, reg);
3673920a5d7SGustavo Pimentel val &= ~PCI_MSI_FLAGS_QMASK;
3683920a5d7SGustavo Pimentel val |= (interrupts << 1) & PCI_MSI_FLAGS_QMASK;
3696e0832faSShawn Lin dw_pcie_dbi_ro_wr_en(pci);
3703920a5d7SGustavo Pimentel dw_pcie_writew_dbi(pci, reg, val);
3716e0832faSShawn Lin dw_pcie_dbi_ro_wr_dis(pci);
3726e0832faSShawn Lin
3736e0832faSShawn Lin return 0;
3746e0832faSShawn Lin }
3756e0832faSShawn Lin
dw_pcie_ep_get_msix(struct pci_epc * epc,u8 func_no,u8 vfunc_no)37653fd3cbeSKishon Vijay Abraham I static int dw_pcie_ep_get_msix(struct pci_epc *epc, u8 func_no, u8 vfunc_no)
377beb4641aSGustavo Pimentel {
378beb4641aSGustavo Pimentel struct dw_pcie_ep *ep = epc_get_drvdata(epc);
379beb4641aSGustavo Pimentel struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
380beb4641aSGustavo Pimentel u32 val, reg;
38124ede430SXiaowei Bao unsigned int func_offset = 0;
38247a06260SXiaowei Bao struct dw_pcie_ep_func *ep_func;
383beb4641aSGustavo Pimentel
38447a06260SXiaowei Bao ep_func = dw_pcie_ep_get_func_from_ep(ep, func_no);
38547a06260SXiaowei Bao if (!ep_func || !ep_func->msix_cap)
386beb4641aSGustavo Pimentel return -EINVAL;
387beb4641aSGustavo Pimentel
38824ede430SXiaowei Bao func_offset = dw_pcie_ep_func_select(ep, func_no);
38924ede430SXiaowei Bao
39047a06260SXiaowei Bao reg = ep_func->msix_cap + func_offset + PCI_MSIX_FLAGS;
391beb4641aSGustavo Pimentel val = dw_pcie_readw_dbi(pci, reg);
392beb4641aSGustavo Pimentel if (!(val & PCI_MSIX_FLAGS_ENABLE))
393beb4641aSGustavo Pimentel return -EINVAL;
394beb4641aSGustavo Pimentel
395beb4641aSGustavo Pimentel val &= PCI_MSIX_FLAGS_QSIZE;
396beb4641aSGustavo Pimentel
397beb4641aSGustavo Pimentel return val;
398beb4641aSGustavo Pimentel }
399beb4641aSGustavo Pimentel
dw_pcie_ep_set_msix(struct pci_epc * epc,u8 func_no,u8 vfunc_no,u16 interrupts,enum pci_barno bir,u32 offset)40053fd3cbeSKishon Vijay Abraham I static int dw_pcie_ep_set_msix(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
40153fd3cbeSKishon Vijay Abraham I u16 interrupts, enum pci_barno bir, u32 offset)
402beb4641aSGustavo Pimentel {
403beb4641aSGustavo Pimentel struct dw_pcie_ep *ep = epc_get_drvdata(epc);
404beb4641aSGustavo Pimentel struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
405beb4641aSGustavo Pimentel u32 val, reg;
40624ede430SXiaowei Bao unsigned int func_offset = 0;
40747a06260SXiaowei Bao struct dw_pcie_ep_func *ep_func;
408beb4641aSGustavo Pimentel
40947a06260SXiaowei Bao ep_func = dw_pcie_ep_get_func_from_ep(ep, func_no);
41047a06260SXiaowei Bao if (!ep_func || !ep_func->msix_cap)
411beb4641aSGustavo Pimentel return -EINVAL;
412beb4641aSGustavo Pimentel
41383153d9fSKishon Vijay Abraham I dw_pcie_dbi_ro_wr_en(pci);
41483153d9fSKishon Vijay Abraham I
41524ede430SXiaowei Bao func_offset = dw_pcie_ep_func_select(ep, func_no);
41624ede430SXiaowei Bao
41747a06260SXiaowei Bao reg = ep_func->msix_cap + func_offset + PCI_MSIX_FLAGS;
418beb4641aSGustavo Pimentel val = dw_pcie_readw_dbi(pci, reg);
419beb4641aSGustavo Pimentel val &= ~PCI_MSIX_FLAGS_QSIZE;
420beb4641aSGustavo Pimentel val |= interrupts;
421beb4641aSGustavo Pimentel dw_pcie_writew_dbi(pci, reg, val);
42283153d9fSKishon Vijay Abraham I
42347a06260SXiaowei Bao reg = ep_func->msix_cap + func_offset + PCI_MSIX_TABLE;
42483153d9fSKishon Vijay Abraham I val = offset | bir;
42583153d9fSKishon Vijay Abraham I dw_pcie_writel_dbi(pci, reg, val);
42683153d9fSKishon Vijay Abraham I
42747a06260SXiaowei Bao reg = ep_func->msix_cap + func_offset + PCI_MSIX_PBA;
42883153d9fSKishon Vijay Abraham I val = (offset + (interrupts * PCI_MSIX_ENTRY_SIZE)) | bir;
42983153d9fSKishon Vijay Abraham I dw_pcie_writel_dbi(pci, reg, val);
43083153d9fSKishon Vijay Abraham I
431beb4641aSGustavo Pimentel dw_pcie_dbi_ro_wr_dis(pci);
432beb4641aSGustavo Pimentel
433beb4641aSGustavo Pimentel return 0;
434beb4641aSGustavo Pimentel }
435beb4641aSGustavo Pimentel
dw_pcie_ep_raise_irq(struct pci_epc * epc,u8 func_no,u8 vfunc_no,enum pci_epc_irq_type type,u16 interrupt_num)43653fd3cbeSKishon Vijay Abraham I static int dw_pcie_ep_raise_irq(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
437d3c70a98SGustavo Pimentel enum pci_epc_irq_type type, u16 interrupt_num)
4386e0832faSShawn Lin {
4396e0832faSShawn Lin struct dw_pcie_ep *ep = epc_get_drvdata(epc);
4406e0832faSShawn Lin
4416e0832faSShawn Lin if (!ep->ops->raise_irq)
4426e0832faSShawn Lin return -EINVAL;
4436e0832faSShawn Lin
4446e0832faSShawn Lin return ep->ops->raise_irq(ep, func_no, type, interrupt_num);
4456e0832faSShawn Lin }
4466e0832faSShawn Lin
dw_pcie_ep_stop(struct pci_epc * epc)4476e0832faSShawn Lin static void dw_pcie_ep_stop(struct pci_epc *epc)
4486e0832faSShawn Lin {
4496e0832faSShawn Lin struct dw_pcie_ep *ep = epc_get_drvdata(epc);
4506e0832faSShawn Lin struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
4516e0832faSShawn Lin
452a37beefbSSerge Semin dw_pcie_stop_link(pci);
4536e0832faSShawn Lin }
4546e0832faSShawn Lin
dw_pcie_ep_start(struct pci_epc * epc)4556e0832faSShawn Lin static int dw_pcie_ep_start(struct pci_epc *epc)
4566e0832faSShawn Lin {
4576e0832faSShawn Lin struct dw_pcie_ep *ep = epc_get_drvdata(epc);
4586e0832faSShawn Lin struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
4596e0832faSShawn Lin
460a37beefbSSerge Semin return dw_pcie_start_link(pci);
4616e0832faSShawn Lin }
4626e0832faSShawn Lin
463fee35cb7SKishon Vijay Abraham I static const struct pci_epc_features*
dw_pcie_ep_get_features(struct pci_epc * epc,u8 func_no,u8 vfunc_no)46453fd3cbeSKishon Vijay Abraham I dw_pcie_ep_get_features(struct pci_epc *epc, u8 func_no, u8 vfunc_no)
465fee35cb7SKishon Vijay Abraham I {
466fee35cb7SKishon Vijay Abraham I struct dw_pcie_ep *ep = epc_get_drvdata(epc);
467fee35cb7SKishon Vijay Abraham I
468fee35cb7SKishon Vijay Abraham I if (!ep->ops->get_features)
469fee35cb7SKishon Vijay Abraham I return NULL;
470fee35cb7SKishon Vijay Abraham I
471fee35cb7SKishon Vijay Abraham I return ep->ops->get_features(ep);
472fee35cb7SKishon Vijay Abraham I }
473fee35cb7SKishon Vijay Abraham I
4746e0832faSShawn Lin static const struct pci_epc_ops epc_ops = {
4756e0832faSShawn Lin .write_header = dw_pcie_ep_write_header,
4766e0832faSShawn Lin .set_bar = dw_pcie_ep_set_bar,
4776e0832faSShawn Lin .clear_bar = dw_pcie_ep_clear_bar,
4786e0832faSShawn Lin .map_addr = dw_pcie_ep_map_addr,
4796e0832faSShawn Lin .unmap_addr = dw_pcie_ep_unmap_addr,
4806e0832faSShawn Lin .set_msi = dw_pcie_ep_set_msi,
4816e0832faSShawn Lin .get_msi = dw_pcie_ep_get_msi,
482beb4641aSGustavo Pimentel .set_msix = dw_pcie_ep_set_msix,
483beb4641aSGustavo Pimentel .get_msix = dw_pcie_ep_get_msix,
4846e0832faSShawn Lin .raise_irq = dw_pcie_ep_raise_irq,
4856e0832faSShawn Lin .start = dw_pcie_ep_start,
4866e0832faSShawn Lin .stop = dw_pcie_ep_stop,
487fee35cb7SKishon Vijay Abraham I .get_features = dw_pcie_ep_get_features,
4886e0832faSShawn Lin };
4896e0832faSShawn Lin
dw_pcie_ep_raise_legacy_irq(struct dw_pcie_ep * ep,u8 func_no)490cb22d40bSGustavo Pimentel int dw_pcie_ep_raise_legacy_irq(struct dw_pcie_ep *ep, u8 func_no)
491cb22d40bSGustavo Pimentel {
492cb22d40bSGustavo Pimentel struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
493cb22d40bSGustavo Pimentel struct device *dev = pci->dev;
494cb22d40bSGustavo Pimentel
495cb22d40bSGustavo Pimentel dev_err(dev, "EP cannot trigger legacy IRQs\n");
496cb22d40bSGustavo Pimentel
497cb22d40bSGustavo Pimentel return -EINVAL;
498cb22d40bSGustavo Pimentel }
499f55fee56SManivannan Sadhasivam EXPORT_SYMBOL_GPL(dw_pcie_ep_raise_legacy_irq);
500cb22d40bSGustavo Pimentel
dw_pcie_ep_raise_msi_irq(struct dw_pcie_ep * ep,u8 func_no,u8 interrupt_num)5016e0832faSShawn Lin int dw_pcie_ep_raise_msi_irq(struct dw_pcie_ep *ep, u8 func_no,
5026e0832faSShawn Lin u8 interrupt_num)
5036e0832faSShawn Lin {
5046e0832faSShawn Lin struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
50547a06260SXiaowei Bao struct dw_pcie_ep_func *ep_func;
5066e0832faSShawn Lin struct pci_epc *epc = ep->epc;
5076b733030SKishon Vijay Abraham I unsigned int aligned_offset;
50824ede430SXiaowei Bao unsigned int func_offset = 0;
5096e0832faSShawn Lin u16 msg_ctrl, msg_data;
5103920a5d7SGustavo Pimentel u32 msg_addr_lower, msg_addr_upper, reg;
5116e0832faSShawn Lin u64 msg_addr;
5126e0832faSShawn Lin bool has_upper;
5136e0832faSShawn Lin int ret;
5146e0832faSShawn Lin
51547a06260SXiaowei Bao ep_func = dw_pcie_ep_get_func_from_ep(ep, func_no);
51647a06260SXiaowei Bao if (!ep_func || !ep_func->msi_cap)
5173920a5d7SGustavo Pimentel return -EINVAL;
5183920a5d7SGustavo Pimentel
51924ede430SXiaowei Bao func_offset = dw_pcie_ep_func_select(ep, func_no);
52024ede430SXiaowei Bao
5216e0832faSShawn Lin /* Raise MSI per the PCI Local Bus Specification Revision 3.0, 6.8.1. */
52247a06260SXiaowei Bao reg = ep_func->msi_cap + func_offset + PCI_MSI_FLAGS;
5233920a5d7SGustavo Pimentel msg_ctrl = dw_pcie_readw_dbi(pci, reg);
5246e0832faSShawn Lin has_upper = !!(msg_ctrl & PCI_MSI_FLAGS_64BIT);
52547a06260SXiaowei Bao reg = ep_func->msi_cap + func_offset + PCI_MSI_ADDRESS_LO;
5263920a5d7SGustavo Pimentel msg_addr_lower = dw_pcie_readl_dbi(pci, reg);
5276e0832faSShawn Lin if (has_upper) {
52847a06260SXiaowei Bao reg = ep_func->msi_cap + func_offset + PCI_MSI_ADDRESS_HI;
5293920a5d7SGustavo Pimentel msg_addr_upper = dw_pcie_readl_dbi(pci, reg);
53047a06260SXiaowei Bao reg = ep_func->msi_cap + func_offset + PCI_MSI_DATA_64;
5313920a5d7SGustavo Pimentel msg_data = dw_pcie_readw_dbi(pci, reg);
5326e0832faSShawn Lin } else {
5336e0832faSShawn Lin msg_addr_upper = 0;
53447a06260SXiaowei Bao reg = ep_func->msi_cap + func_offset + PCI_MSI_DATA_32;
5353920a5d7SGustavo Pimentel msg_data = dw_pcie_readw_dbi(pci, reg);
5366e0832faSShawn Lin }
537d45e3c1aSLad Prabhakar aligned_offset = msg_addr_lower & (epc->mem->window.page_size - 1);
5386b733030SKishon Vijay Abraham I msg_addr = ((u64)msg_addr_upper) << 32 |
5396b733030SKishon Vijay Abraham I (msg_addr_lower & ~aligned_offset);
54053fd3cbeSKishon Vijay Abraham I ret = dw_pcie_ep_map_addr(epc, func_no, 0, ep->msi_mem_phys, msg_addr,
541d45e3c1aSLad Prabhakar epc->mem->window.page_size);
5426e0832faSShawn Lin if (ret)
5436e0832faSShawn Lin return ret;
5446e0832faSShawn Lin
5456b733030SKishon Vijay Abraham I writel(msg_data | (interrupt_num - 1), ep->msi_mem + aligned_offset);
5466e0832faSShawn Lin
54753fd3cbeSKishon Vijay Abraham I dw_pcie_ep_unmap_addr(epc, func_no, 0, ep->msi_mem_phys);
5486e0832faSShawn Lin
5496e0832faSShawn Lin return 0;
5506e0832faSShawn Lin }
551f55fee56SManivannan Sadhasivam EXPORT_SYMBOL_GPL(dw_pcie_ep_raise_msi_irq);
5526e0832faSShawn Lin
dw_pcie_ep_raise_msix_irq_doorbell(struct dw_pcie_ep * ep,u8 func_no,u16 interrupt_num)5532f7f7001SXiaowei Bao int dw_pcie_ep_raise_msix_irq_doorbell(struct dw_pcie_ep *ep, u8 func_no,
5542f7f7001SXiaowei Bao u16 interrupt_num)
5552f7f7001SXiaowei Bao {
5562f7f7001SXiaowei Bao struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
5572f7f7001SXiaowei Bao struct dw_pcie_ep_func *ep_func;
5582f7f7001SXiaowei Bao u32 msg_data;
5592f7f7001SXiaowei Bao
5602f7f7001SXiaowei Bao ep_func = dw_pcie_ep_get_func_from_ep(ep, func_no);
5612f7f7001SXiaowei Bao if (!ep_func || !ep_func->msix_cap)
5622f7f7001SXiaowei Bao return -EINVAL;
5632f7f7001SXiaowei Bao
5642f7f7001SXiaowei Bao msg_data = (func_no << PCIE_MSIX_DOORBELL_PF_SHIFT) |
5652f7f7001SXiaowei Bao (interrupt_num - 1);
5662f7f7001SXiaowei Bao
5672f7f7001SXiaowei Bao dw_pcie_writel_dbi(pci, PCIE_MSIX_DOORBELL, msg_data);
5682f7f7001SXiaowei Bao
5692f7f7001SXiaowei Bao return 0;
5702f7f7001SXiaowei Bao }
5712f7f7001SXiaowei Bao
dw_pcie_ep_raise_msix_irq(struct dw_pcie_ep * ep,u8 func_no,u16 interrupt_num)572beb4641aSGustavo Pimentel int dw_pcie_ep_raise_msix_irq(struct dw_pcie_ep *ep, u8 func_no,
573beb4641aSGustavo Pimentel u16 interrupt_num)
574beb4641aSGustavo Pimentel {
575beb4641aSGustavo Pimentel struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
57647a06260SXiaowei Bao struct dw_pcie_ep_func *ep_func;
5776f5e193bSKishon Vijay Abraham I struct pci_epf_msix_tbl *msix_tbl;
578beb4641aSGustavo Pimentel struct pci_epc *epc = ep->epc;
57924ede430SXiaowei Bao unsigned int func_offset = 0;
580beb4641aSGustavo Pimentel u32 reg, msg_data, vec_ctrl;
5816f5e193bSKishon Vijay Abraham I unsigned int aligned_offset;
5826f5e193bSKishon Vijay Abraham I u32 tbl_offset;
5836f5e193bSKishon Vijay Abraham I u64 msg_addr;
584beb4641aSGustavo Pimentel int ret;
5856f5e193bSKishon Vijay Abraham I u8 bir;
586beb4641aSGustavo Pimentel
58747a06260SXiaowei Bao ep_func = dw_pcie_ep_get_func_from_ep(ep, func_no);
58847a06260SXiaowei Bao if (!ep_func || !ep_func->msix_cap)
58947a06260SXiaowei Bao return -EINVAL;
59047a06260SXiaowei Bao
59124ede430SXiaowei Bao func_offset = dw_pcie_ep_func_select(ep, func_no);
59224ede430SXiaowei Bao
59347a06260SXiaowei Bao reg = ep_func->msix_cap + func_offset + PCI_MSIX_TABLE;
594beb4641aSGustavo Pimentel tbl_offset = dw_pcie_readl_dbi(pci, reg);
595beb4641aSGustavo Pimentel bir = (tbl_offset & PCI_MSIX_TABLE_BIR);
596beb4641aSGustavo Pimentel tbl_offset &= PCI_MSIX_TABLE_OFFSET;
597beb4641aSGustavo Pimentel
598bf711620SJiri Slaby msix_tbl = ep->epf_bar[bir]->addr + tbl_offset;
5996f5e193bSKishon Vijay Abraham I msg_addr = msix_tbl[(interrupt_num - 1)].msg_addr;
6006f5e193bSKishon Vijay Abraham I msg_data = msix_tbl[(interrupt_num - 1)].msg_data;
6016f5e193bSKishon Vijay Abraham I vec_ctrl = msix_tbl[(interrupt_num - 1)].vector_ctrl;
602beb4641aSGustavo Pimentel
6030380cf84SGustavo Pimentel if (vec_ctrl & PCI_MSIX_ENTRY_CTRL_MASKBIT) {
6040380cf84SGustavo Pimentel dev_dbg(pci->dev, "MSI-X entry ctrl set\n");
605beb4641aSGustavo Pimentel return -EPERM;
6060380cf84SGustavo Pimentel }
607beb4641aSGustavo Pimentel
608d45e3c1aSLad Prabhakar aligned_offset = msg_addr & (epc->mem->window.page_size - 1);
6096ea2f3b9SDan Carpenter msg_addr = ALIGN_DOWN(msg_addr, epc->mem->window.page_size);
61053fd3cbeSKishon Vijay Abraham I ret = dw_pcie_ep_map_addr(epc, func_no, 0, ep->msi_mem_phys, msg_addr,
611d45e3c1aSLad Prabhakar epc->mem->window.page_size);
612beb4641aSGustavo Pimentel if (ret)
613beb4641aSGustavo Pimentel return ret;
614beb4641aSGustavo Pimentel
6156f5e193bSKishon Vijay Abraham I writel(msg_data, ep->msi_mem + aligned_offset);
616beb4641aSGustavo Pimentel
61753fd3cbeSKishon Vijay Abraham I dw_pcie_ep_unmap_addr(epc, func_no, 0, ep->msi_mem_phys);
618beb4641aSGustavo Pimentel
619beb4641aSGustavo Pimentel return 0;
620beb4641aSGustavo Pimentel }
621beb4641aSGustavo Pimentel
dw_pcie_ep_exit(struct dw_pcie_ep * ep)6226e0832faSShawn Lin void dw_pcie_ep_exit(struct dw_pcie_ep *ep)
6236e0832faSShawn Lin {
624939fbcd5SSerge Semin struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
6256e0832faSShawn Lin struct pci_epc *epc = ep->epc;
6266e0832faSShawn Lin
627939fbcd5SSerge Semin dw_pcie_edma_remove(pci);
628939fbcd5SSerge Semin
6296e0832faSShawn Lin pci_epc_mem_free_addr(epc, ep->msi_mem_phys, ep->msi_mem,
630d45e3c1aSLad Prabhakar epc->mem->window.page_size);
6316e0832faSShawn Lin
6326e0832faSShawn Lin pci_epc_mem_exit(epc);
6336e0832faSShawn Lin }
6346e0832faSShawn Lin
dw_pcie_ep_find_ext_capability(struct dw_pcie * pci,int cap)635fc9a7704SKishon Vijay Abraham I static unsigned int dw_pcie_ep_find_ext_capability(struct dw_pcie *pci, int cap)
636fc9a7704SKishon Vijay Abraham I {
637fc9a7704SKishon Vijay Abraham I u32 header;
638fc9a7704SKishon Vijay Abraham I int pos = PCI_CFG_SPACE_SIZE;
639fc9a7704SKishon Vijay Abraham I
640fc9a7704SKishon Vijay Abraham I while (pos) {
641fc9a7704SKishon Vijay Abraham I header = dw_pcie_readl_dbi(pci, pos);
642fc9a7704SKishon Vijay Abraham I if (PCI_EXT_CAP_ID(header) == cap)
643fc9a7704SKishon Vijay Abraham I return pos;
644fc9a7704SKishon Vijay Abraham I
645fc9a7704SKishon Vijay Abraham I pos = PCI_EXT_CAP_NEXT(header);
646fc9a7704SKishon Vijay Abraham I if (!pos)
647fc9a7704SKishon Vijay Abraham I break;
648fc9a7704SKishon Vijay Abraham I }
649fc9a7704SKishon Vijay Abraham I
650fc9a7704SKishon Vijay Abraham I return 0;
651fc9a7704SKishon Vijay Abraham I }
652fc9a7704SKishon Vijay Abraham I
dw_pcie_ep_init_complete(struct dw_pcie_ep * ep)653e966f739SVidya Sagar int dw_pcie_ep_init_complete(struct dw_pcie_ep *ep)
654e966f739SVidya Sagar {
655e966f739SVidya Sagar struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
656442ae919SVidya Sagar unsigned int offset, ptm_cap_base;
657e966f739SVidya Sagar unsigned int nbars;
658e966f739SVidya Sagar u8 hdr_type;
659e966f739SVidya Sagar u32 reg;
660e966f739SVidya Sagar int i;
661e966f739SVidya Sagar
66216270a92SHou Zhiqiang hdr_type = dw_pcie_readb_dbi(pci, PCI_HEADER_TYPE) &
66316270a92SHou Zhiqiang PCI_HEADER_TYPE_MASK;
664e966f739SVidya Sagar if (hdr_type != PCI_HEADER_TYPE_NORMAL) {
665e966f739SVidya Sagar dev_err(pci->dev,
666e966f739SVidya Sagar "PCIe controller is not set to EP mode (hdr_type:0x%x)!\n",
667e966f739SVidya Sagar hdr_type);
668e966f739SVidya Sagar return -EIO;
669e966f739SVidya Sagar }
670e966f739SVidya Sagar
671e966f739SVidya Sagar offset = dw_pcie_ep_find_ext_capability(pci, PCI_EXT_CAP_ID_REBAR);
672442ae919SVidya Sagar ptm_cap_base = dw_pcie_ep_find_ext_capability(pci, PCI_EXT_CAP_ID_PTM);
67339bc5006SRob Herring
67439bc5006SRob Herring dw_pcie_dbi_ro_wr_en(pci);
67539bc5006SRob Herring
676e966f739SVidya Sagar if (offset) {
677e966f739SVidya Sagar reg = dw_pcie_readl_dbi(pci, offset + PCI_REBAR_CTRL);
678e966f739SVidya Sagar nbars = (reg & PCI_REBAR_CTRL_NBAR_MASK) >>
679e966f739SVidya Sagar PCI_REBAR_CTRL_NBAR_SHIFT;
680e966f739SVidya Sagar
681cebb4baeSNiklas Cassel /*
682cebb4baeSNiklas Cassel * PCIe r6.0, sec 7.8.6.2 require us to support at least one
683cebb4baeSNiklas Cassel * size in the range from 1 MB to 512 GB. Advertise support
684cebb4baeSNiklas Cassel * for 1 MB BAR size only.
685cebb4baeSNiklas Cassel */
686e966f739SVidya Sagar for (i = 0; i < nbars; i++, offset += PCI_REBAR_CTRL)
687cebb4baeSNiklas Cassel dw_pcie_writel_dbi(pci, offset + PCI_REBAR_CAP, BIT(4));
688e966f739SVidya Sagar }
689e966f739SVidya Sagar
690442ae919SVidya Sagar /*
691442ae919SVidya Sagar * PTM responder capability can be disabled only after disabling
692442ae919SVidya Sagar * PTM root capability.
693442ae919SVidya Sagar */
694442ae919SVidya Sagar if (ptm_cap_base) {
695442ae919SVidya Sagar dw_pcie_dbi_ro_wr_en(pci);
696442ae919SVidya Sagar reg = dw_pcie_readl_dbi(pci, ptm_cap_base + PCI_PTM_CAP);
697442ae919SVidya Sagar reg &= ~PCI_PTM_CAP_ROOT;
698442ae919SVidya Sagar dw_pcie_writel_dbi(pci, ptm_cap_base + PCI_PTM_CAP, reg);
699442ae919SVidya Sagar
700442ae919SVidya Sagar reg = dw_pcie_readl_dbi(pci, ptm_cap_base + PCI_PTM_CAP);
701442ae919SVidya Sagar reg &= ~(PCI_PTM_CAP_RES | PCI_PTM_GRANULARITY_MASK);
702442ae919SVidya Sagar dw_pcie_writel_dbi(pci, ptm_cap_base + PCI_PTM_CAP, reg);
703442ae919SVidya Sagar dw_pcie_dbi_ro_wr_dis(pci);
704442ae919SVidya Sagar }
705442ae919SVidya Sagar
706e966f739SVidya Sagar dw_pcie_setup(pci);
70739bc5006SRob Herring dw_pcie_dbi_ro_wr_dis(pci);
708e966f739SVidya Sagar
709e966f739SVidya Sagar return 0;
710e966f739SVidya Sagar }
711c57247f9SVidya Sagar EXPORT_SYMBOL_GPL(dw_pcie_ep_init_complete);
712e966f739SVidya Sagar
dw_pcie_ep_init(struct dw_pcie_ep * ep)7136e0832faSShawn Lin int dw_pcie_ep_init(struct dw_pcie_ep *ep)
7146e0832faSShawn Lin {
7156e0832faSShawn Lin int ret;
7166e0832faSShawn Lin void *addr;
71747a06260SXiaowei Bao u8 func_no;
718a0fd361dSRob Herring struct resource *res;
7196e0832faSShawn Lin struct pci_epc *epc;
7206e0832faSShawn Lin struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
7216e0832faSShawn Lin struct device *dev = pci->dev;
722a0fd361dSRob Herring struct platform_device *pdev = to_platform_device(dev);
7236e0832faSShawn Lin struct device_node *np = dev->of_node;
724e966f739SVidya Sagar const struct pci_epc_features *epc_features;
72547a06260SXiaowei Bao struct dw_pcie_ep_func *ep_func;
72647a06260SXiaowei Bao
72747a06260SXiaowei Bao INIT_LIST_HEAD(&ep->func_list);
7286e0832faSShawn Lin
729ef8c5887SSerge Semin ret = dw_pcie_get_resources(pci);
730ef8c5887SSerge Semin if (ret)
731ef8c5887SSerge Semin return ret;
732a0fd361dSRob Herring
733a0fd361dSRob Herring res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "addr_space");
734a0fd361dSRob Herring if (!res)
735a0fd361dSRob Herring return -EINVAL;
736a0fd361dSRob Herring
737a0fd361dSRob Herring ep->phys_base = res->start;
738a0fd361dSRob Herring ep->addr_size = resource_size(res);
739a0fd361dSRob Herring
74013e9d390SSerge Semin dw_pcie_version_detect(pci);
74113e9d390SSerge Semin
742e3dc79adSSerge Semin dw_pcie_iatu_detect(pci);
743e3dc79adSSerge Semin
7446be6f852SChristophe JAILLET ep->ib_window_map = devm_bitmap_zalloc(dev, pci->num_ib_windows,
7456e0832faSShawn Lin GFP_KERNEL);
7466e0832faSShawn Lin if (!ep->ib_window_map)
7476e0832faSShawn Lin return -ENOMEM;
7486e0832faSShawn Lin
7496be6f852SChristophe JAILLET ep->ob_window_map = devm_bitmap_zalloc(dev, pci->num_ob_windows,
7506e0832faSShawn Lin GFP_KERNEL);
7516e0832faSShawn Lin if (!ep->ob_window_map)
7526e0832faSShawn Lin return -ENOMEM;
7536e0832faSShawn Lin
7549ca17af5SRob Herring addr = devm_kcalloc(dev, pci->num_ob_windows, sizeof(phys_addr_t),
7556e0832faSShawn Lin GFP_KERNEL);
7566e0832faSShawn Lin if (!addr)
7576e0832faSShawn Lin return -ENOMEM;
7586e0832faSShawn Lin ep->outbound_addr = addr;
7596e0832faSShawn Lin
7606e0832faSShawn Lin epc = devm_pci_epc_create(dev, &epc_ops);
7616e0832faSShawn Lin if (IS_ERR(epc)) {
7626e0832faSShawn Lin dev_err(dev, "Failed to create epc device\n");
7636e0832faSShawn Lin return PTR_ERR(epc);
7646e0832faSShawn Lin }
7656e0832faSShawn Lin
7664e965edeSGustavo Pimentel ep->epc = epc;
7674e965edeSGustavo Pimentel epc_set_drvdata(epc, ep);
7684e965edeSGustavo Pimentel
7696e0832faSShawn Lin ret = of_property_read_u8(np, "max-functions", &epc->max_functions);
7706e0832faSShawn Lin if (ret < 0)
7716e0832faSShawn Lin epc->max_functions = 1;
7726e0832faSShawn Lin
77347a06260SXiaowei Bao for (func_no = 0; func_no < epc->max_functions; func_no++) {
77447a06260SXiaowei Bao ep_func = devm_kzalloc(dev, sizeof(*ep_func), GFP_KERNEL);
77547a06260SXiaowei Bao if (!ep_func)
77647a06260SXiaowei Bao return -ENOMEM;
7776bfc9c3aSXiaowei Bao
77847a06260SXiaowei Bao ep_func->func_no = func_no;
77947a06260SXiaowei Bao ep_func->msi_cap = dw_pcie_ep_find_capability(ep, func_no,
78047a06260SXiaowei Bao PCI_CAP_ID_MSI);
78147a06260SXiaowei Bao ep_func->msix_cap = dw_pcie_ep_find_capability(ep, func_no,
78247a06260SXiaowei Bao PCI_CAP_ID_MSIX);
78347a06260SXiaowei Bao
78447a06260SXiaowei Bao list_add_tail(&ep_func->list, &ep->func_list);
78547a06260SXiaowei Bao }
7866bfc9c3aSXiaowei Bao
78724ede430SXiaowei Bao if (ep->ops->ep_init)
78824ede430SXiaowei Bao ep->ops->ep_init(ep);
78924ede430SXiaowei Bao
790d45e3c1aSLad Prabhakar ret = pci_epc_mem_init(epc, ep->phys_base, ep->addr_size,
7916e0832faSShawn Lin ep->page_size);
7926e0832faSShawn Lin if (ret < 0) {
7936e0832faSShawn Lin dev_err(dev, "Failed to initialize address space\n");
7946e0832faSShawn Lin return ret;
7956e0832faSShawn Lin }
7966e0832faSShawn Lin
7976e0832faSShawn Lin ep->msi_mem = pci_epc_mem_alloc_addr(epc, &ep->msi_mem_phys,
798d45e3c1aSLad Prabhakar epc->mem->window.page_size);
7996e0832faSShawn Lin if (!ep->msi_mem) {
8008161e962SSerge Semin ret = -ENOMEM;
801beb4641aSGustavo Pimentel dev_err(dev, "Failed to reserve memory for MSI/MSI-X\n");
8028161e962SSerge Semin goto err_exit_epc_mem;
8036e0832faSShawn Lin }
804beb4641aSGustavo Pimentel
805939fbcd5SSerge Semin ret = dw_pcie_edma_detect(pci);
806939fbcd5SSerge Semin if (ret)
807939fbcd5SSerge Semin goto err_free_epc_mem;
808939fbcd5SSerge Semin
809e966f739SVidya Sagar if (ep->ops->get_features) {
810e966f739SVidya Sagar epc_features = ep->ops->get_features(ep);
811e966f739SVidya Sagar if (epc_features->core_init_notifier)
812e966f739SVidya Sagar return 0;
813fc9a7704SKishon Vijay Abraham I }
814fc9a7704SKishon Vijay Abraham I
8158161e962SSerge Semin ret = dw_pcie_ep_init_complete(ep);
8168161e962SSerge Semin if (ret)
817939fbcd5SSerge Semin goto err_remove_edma;
8188161e962SSerge Semin
8198161e962SSerge Semin return 0;
8208161e962SSerge Semin
821939fbcd5SSerge Semin err_remove_edma:
822939fbcd5SSerge Semin dw_pcie_edma_remove(pci);
823939fbcd5SSerge Semin
8248161e962SSerge Semin err_free_epc_mem:
8258161e962SSerge Semin pci_epc_mem_free_addr(epc, ep->msi_mem_phys, ep->msi_mem,
8268161e962SSerge Semin epc->mem->window.page_size);
8278161e962SSerge Semin
8288161e962SSerge Semin err_exit_epc_mem:
8298161e962SSerge Semin pci_epc_mem_exit(epc);
8308161e962SSerge Semin
8318161e962SSerge Semin return ret;
8326e0832faSShawn Lin }
833c57247f9SVidya Sagar EXPORT_SYMBOL_GPL(dw_pcie_ep_init);
834