18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Synopsys DesignWare PCIe Endpoint controller driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2017 Texas Instruments
68c2ecf20Sopenharmony_ci * Author: Kishon Vijay Abraham I <kishon@ti.com>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/kernel.h>
108c2ecf20Sopenharmony_ci#include <linux/of.h>
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include "pcie-designware.h"
138c2ecf20Sopenharmony_ci#include <linux/pci-epc.h>
148c2ecf20Sopenharmony_ci#include <linux/pci-epf.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include "../../pci.h"
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_civoid dw_pcie_ep_linkup(struct dw_pcie_ep *ep)
198c2ecf20Sopenharmony_ci{
208c2ecf20Sopenharmony_ci	struct pci_epc *epc = ep->epc;
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci	pci_epc_linkup(epc);
238c2ecf20Sopenharmony_ci}
248c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(dw_pcie_ep_linkup);
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_civoid dw_pcie_ep_init_notify(struct dw_pcie_ep *ep)
278c2ecf20Sopenharmony_ci{
288c2ecf20Sopenharmony_ci	struct pci_epc *epc = ep->epc;
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci	pci_epc_init_notify(epc);
318c2ecf20Sopenharmony_ci}
328c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(dw_pcie_ep_init_notify);
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_cistruct dw_pcie_ep_func *
358c2ecf20Sopenharmony_cidw_pcie_ep_get_func_from_ep(struct dw_pcie_ep *ep, u8 func_no)
368c2ecf20Sopenharmony_ci{
378c2ecf20Sopenharmony_ci	struct dw_pcie_ep_func *ep_func;
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci	list_for_each_entry(ep_func, &ep->func_list, list) {
408c2ecf20Sopenharmony_ci		if (ep_func->func_no == func_no)
418c2ecf20Sopenharmony_ci			return ep_func;
428c2ecf20Sopenharmony_ci	}
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci	return NULL;
458c2ecf20Sopenharmony_ci}
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_cistatic unsigned int dw_pcie_ep_func_select(struct dw_pcie_ep *ep, u8 func_no)
488c2ecf20Sopenharmony_ci{
498c2ecf20Sopenharmony_ci	unsigned int func_offset = 0;
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	if (ep->ops->func_conf_select)
528c2ecf20Sopenharmony_ci		func_offset = ep->ops->func_conf_select(ep, func_no);
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	return func_offset;
558c2ecf20Sopenharmony_ci}
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_cistatic void __dw_pcie_ep_reset_bar(struct dw_pcie *pci, u8 func_no,
588c2ecf20Sopenharmony_ci				   enum pci_barno bar, int flags)
598c2ecf20Sopenharmony_ci{
608c2ecf20Sopenharmony_ci	u32 reg;
618c2ecf20Sopenharmony_ci	unsigned int func_offset = 0;
628c2ecf20Sopenharmony_ci	struct dw_pcie_ep *ep = &pci->ep;
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	func_offset = dw_pcie_ep_func_select(ep, func_no);
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	reg = func_offset + PCI_BASE_ADDRESS_0 + (4 * bar);
678c2ecf20Sopenharmony_ci	dw_pcie_dbi_ro_wr_en(pci);
688c2ecf20Sopenharmony_ci	dw_pcie_writel_dbi2(pci, reg, 0x0);
698c2ecf20Sopenharmony_ci	dw_pcie_writel_dbi(pci, reg, 0x0);
708c2ecf20Sopenharmony_ci	if (flags & PCI_BASE_ADDRESS_MEM_TYPE_64) {
718c2ecf20Sopenharmony_ci		dw_pcie_writel_dbi2(pci, reg + 4, 0x0);
728c2ecf20Sopenharmony_ci		dw_pcie_writel_dbi(pci, reg + 4, 0x0);
738c2ecf20Sopenharmony_ci	}
748c2ecf20Sopenharmony_ci	dw_pcie_dbi_ro_wr_dis(pci);
758c2ecf20Sopenharmony_ci}
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_civoid dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar)
788c2ecf20Sopenharmony_ci{
798c2ecf20Sopenharmony_ci	u8 func_no, funcs;
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	funcs = pci->ep.epc->max_functions;
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	for (func_no = 0; func_no < funcs; func_no++)
848c2ecf20Sopenharmony_ci		__dw_pcie_ep_reset_bar(pci, func_no, bar, 0);
858c2ecf20Sopenharmony_ci}
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_cistatic u8 __dw_pcie_ep_find_next_cap(struct dw_pcie_ep *ep, u8 func_no,
888c2ecf20Sopenharmony_ci		u8 cap_ptr, u8 cap)
898c2ecf20Sopenharmony_ci{
908c2ecf20Sopenharmony_ci	struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
918c2ecf20Sopenharmony_ci	unsigned int func_offset = 0;
928c2ecf20Sopenharmony_ci	u8 cap_id, next_cap_ptr;
938c2ecf20Sopenharmony_ci	u16 reg;
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	if (!cap_ptr)
968c2ecf20Sopenharmony_ci		return 0;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	func_offset = dw_pcie_ep_func_select(ep, func_no);
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	reg = dw_pcie_readw_dbi(pci, func_offset + cap_ptr);
1018c2ecf20Sopenharmony_ci	cap_id = (reg & 0x00ff);
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	if (cap_id > PCI_CAP_ID_MAX)
1048c2ecf20Sopenharmony_ci		return 0;
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	if (cap_id == cap)
1078c2ecf20Sopenharmony_ci		return cap_ptr;
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	next_cap_ptr = (reg & 0xff00) >> 8;
1108c2ecf20Sopenharmony_ci	return __dw_pcie_ep_find_next_cap(ep, func_no, next_cap_ptr, cap);
1118c2ecf20Sopenharmony_ci}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_cistatic u8 dw_pcie_ep_find_capability(struct dw_pcie_ep *ep, u8 func_no, u8 cap)
1148c2ecf20Sopenharmony_ci{
1158c2ecf20Sopenharmony_ci	struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
1168c2ecf20Sopenharmony_ci	unsigned int func_offset = 0;
1178c2ecf20Sopenharmony_ci	u8 next_cap_ptr;
1188c2ecf20Sopenharmony_ci	u16 reg;
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	func_offset = dw_pcie_ep_func_select(ep, func_no);
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	reg = dw_pcie_readw_dbi(pci, func_offset + PCI_CAPABILITY_LIST);
1238c2ecf20Sopenharmony_ci	next_cap_ptr = (reg & 0x00ff);
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	return __dw_pcie_ep_find_next_cap(ep, func_no, next_cap_ptr, cap);
1268c2ecf20Sopenharmony_ci}
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_cistatic int dw_pcie_ep_write_header(struct pci_epc *epc, u8 func_no,
1298c2ecf20Sopenharmony_ci				   struct pci_epf_header *hdr)
1308c2ecf20Sopenharmony_ci{
1318c2ecf20Sopenharmony_ci	struct dw_pcie_ep *ep = epc_get_drvdata(epc);
1328c2ecf20Sopenharmony_ci	struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
1338c2ecf20Sopenharmony_ci	unsigned int func_offset = 0;
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	func_offset = dw_pcie_ep_func_select(ep, func_no);
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	dw_pcie_dbi_ro_wr_en(pci);
1388c2ecf20Sopenharmony_ci	dw_pcie_writew_dbi(pci, func_offset + PCI_VENDOR_ID, hdr->vendorid);
1398c2ecf20Sopenharmony_ci	dw_pcie_writew_dbi(pci, func_offset + PCI_DEVICE_ID, hdr->deviceid);
1408c2ecf20Sopenharmony_ci	dw_pcie_writeb_dbi(pci, func_offset + PCI_REVISION_ID, hdr->revid);
1418c2ecf20Sopenharmony_ci	dw_pcie_writeb_dbi(pci, func_offset + PCI_CLASS_PROG, hdr->progif_code);
1428c2ecf20Sopenharmony_ci	dw_pcie_writew_dbi(pci, func_offset + PCI_CLASS_DEVICE,
1438c2ecf20Sopenharmony_ci			   hdr->subclass_code | hdr->baseclass_code << 8);
1448c2ecf20Sopenharmony_ci	dw_pcie_writeb_dbi(pci, func_offset + PCI_CACHE_LINE_SIZE,
1458c2ecf20Sopenharmony_ci			   hdr->cache_line_size);
1468c2ecf20Sopenharmony_ci	dw_pcie_writew_dbi(pci, func_offset + PCI_SUBSYSTEM_VENDOR_ID,
1478c2ecf20Sopenharmony_ci			   hdr->subsys_vendor_id);
1488c2ecf20Sopenharmony_ci	dw_pcie_writew_dbi(pci, func_offset + PCI_SUBSYSTEM_ID, hdr->subsys_id);
1498c2ecf20Sopenharmony_ci	dw_pcie_writeb_dbi(pci, func_offset + PCI_INTERRUPT_PIN,
1508c2ecf20Sopenharmony_ci			   hdr->interrupt_pin);
1518c2ecf20Sopenharmony_ci	dw_pcie_dbi_ro_wr_dis(pci);
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	return 0;
1548c2ecf20Sopenharmony_ci}
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_cistatic int dw_pcie_ep_inbound_atu(struct dw_pcie_ep *ep, u8 func_no,
1578c2ecf20Sopenharmony_ci				  enum pci_barno bar, dma_addr_t cpu_addr,
1588c2ecf20Sopenharmony_ci				  enum dw_pcie_as_type as_type)
1598c2ecf20Sopenharmony_ci{
1608c2ecf20Sopenharmony_ci	int ret;
1618c2ecf20Sopenharmony_ci	u32 free_win;
1628c2ecf20Sopenharmony_ci	struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	free_win = find_first_zero_bit(ep->ib_window_map, ep->num_ib_windows);
1658c2ecf20Sopenharmony_ci	if (free_win >= ep->num_ib_windows) {
1668c2ecf20Sopenharmony_ci		dev_err(pci->dev, "No free inbound window\n");
1678c2ecf20Sopenharmony_ci		return -EINVAL;
1688c2ecf20Sopenharmony_ci	}
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	ret = dw_pcie_prog_inbound_atu(pci, func_no, free_win, bar, cpu_addr,
1718c2ecf20Sopenharmony_ci				       as_type);
1728c2ecf20Sopenharmony_ci	if (ret < 0) {
1738c2ecf20Sopenharmony_ci		dev_err(pci->dev, "Failed to program IB window\n");
1748c2ecf20Sopenharmony_ci		return ret;
1758c2ecf20Sopenharmony_ci	}
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	ep->bar_to_atu[bar] = free_win;
1788c2ecf20Sopenharmony_ci	set_bit(free_win, ep->ib_window_map);
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	return 0;
1818c2ecf20Sopenharmony_ci}
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_cistatic int dw_pcie_ep_outbound_atu(struct dw_pcie_ep *ep, u8 func_no,
1848c2ecf20Sopenharmony_ci				   phys_addr_t phys_addr,
1858c2ecf20Sopenharmony_ci				   u64 pci_addr, size_t size)
1868c2ecf20Sopenharmony_ci{
1878c2ecf20Sopenharmony_ci	u32 free_win;
1888c2ecf20Sopenharmony_ci	struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	free_win = find_first_zero_bit(ep->ob_window_map, ep->num_ob_windows);
1918c2ecf20Sopenharmony_ci	if (free_win >= ep->num_ob_windows) {
1928c2ecf20Sopenharmony_ci		dev_err(pci->dev, "No free outbound window\n");
1938c2ecf20Sopenharmony_ci		return -EINVAL;
1948c2ecf20Sopenharmony_ci	}
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	dw_pcie_prog_ep_outbound_atu(pci, func_no, free_win, PCIE_ATU_TYPE_MEM,
1978c2ecf20Sopenharmony_ci				     phys_addr, pci_addr, size);
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	set_bit(free_win, ep->ob_window_map);
2008c2ecf20Sopenharmony_ci	ep->outbound_addr[free_win] = phys_addr;
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	return 0;
2038c2ecf20Sopenharmony_ci}
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_cistatic void dw_pcie_ep_clear_bar(struct pci_epc *epc, u8 func_no,
2068c2ecf20Sopenharmony_ci				 struct pci_epf_bar *epf_bar)
2078c2ecf20Sopenharmony_ci{
2088c2ecf20Sopenharmony_ci	struct dw_pcie_ep *ep = epc_get_drvdata(epc);
2098c2ecf20Sopenharmony_ci	struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
2108c2ecf20Sopenharmony_ci	enum pci_barno bar = epf_bar->barno;
2118c2ecf20Sopenharmony_ci	u32 atu_index = ep->bar_to_atu[bar];
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	__dw_pcie_ep_reset_bar(pci, func_no, bar, epf_bar->flags);
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	dw_pcie_disable_atu(pci, atu_index, DW_PCIE_REGION_INBOUND);
2168c2ecf20Sopenharmony_ci	clear_bit(atu_index, ep->ib_window_map);
2178c2ecf20Sopenharmony_ci	ep->epf_bar[bar] = NULL;
2188c2ecf20Sopenharmony_ci}
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_cistatic int dw_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no,
2218c2ecf20Sopenharmony_ci			      struct pci_epf_bar *epf_bar)
2228c2ecf20Sopenharmony_ci{
2238c2ecf20Sopenharmony_ci	int ret;
2248c2ecf20Sopenharmony_ci	struct dw_pcie_ep *ep = epc_get_drvdata(epc);
2258c2ecf20Sopenharmony_ci	struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
2268c2ecf20Sopenharmony_ci	enum pci_barno bar = epf_bar->barno;
2278c2ecf20Sopenharmony_ci	size_t size = epf_bar->size;
2288c2ecf20Sopenharmony_ci	int flags = epf_bar->flags;
2298c2ecf20Sopenharmony_ci	enum dw_pcie_as_type as_type;
2308c2ecf20Sopenharmony_ci	u32 reg;
2318c2ecf20Sopenharmony_ci	unsigned int func_offset = 0;
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	func_offset = dw_pcie_ep_func_select(ep, func_no);
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	reg = PCI_BASE_ADDRESS_0 + (4 * bar) + func_offset;
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	if (!(flags & PCI_BASE_ADDRESS_SPACE))
2388c2ecf20Sopenharmony_ci		as_type = DW_PCIE_AS_MEM;
2398c2ecf20Sopenharmony_ci	else
2408c2ecf20Sopenharmony_ci		as_type = DW_PCIE_AS_IO;
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	ret = dw_pcie_ep_inbound_atu(ep, func_no, bar,
2438c2ecf20Sopenharmony_ci				     epf_bar->phys_addr, as_type);
2448c2ecf20Sopenharmony_ci	if (ret)
2458c2ecf20Sopenharmony_ci		return ret;
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	dw_pcie_dbi_ro_wr_en(pci);
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	dw_pcie_writel_dbi2(pci, reg, lower_32_bits(size - 1));
2508c2ecf20Sopenharmony_ci	dw_pcie_writel_dbi(pci, reg, flags);
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	if (flags & PCI_BASE_ADDRESS_MEM_TYPE_64) {
2538c2ecf20Sopenharmony_ci		dw_pcie_writel_dbi2(pci, reg + 4, upper_32_bits(size - 1));
2548c2ecf20Sopenharmony_ci		dw_pcie_writel_dbi(pci, reg + 4, 0);
2558c2ecf20Sopenharmony_ci	}
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	ep->epf_bar[bar] = epf_bar;
2588c2ecf20Sopenharmony_ci	dw_pcie_dbi_ro_wr_dis(pci);
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	return 0;
2618c2ecf20Sopenharmony_ci}
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_cistatic int dw_pcie_find_index(struct dw_pcie_ep *ep, phys_addr_t addr,
2648c2ecf20Sopenharmony_ci			      u32 *atu_index)
2658c2ecf20Sopenharmony_ci{
2668c2ecf20Sopenharmony_ci	u32 index;
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	for (index = 0; index < ep->num_ob_windows; index++) {
2698c2ecf20Sopenharmony_ci		if (ep->outbound_addr[index] != addr)
2708c2ecf20Sopenharmony_ci			continue;
2718c2ecf20Sopenharmony_ci		*atu_index = index;
2728c2ecf20Sopenharmony_ci		return 0;
2738c2ecf20Sopenharmony_ci	}
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	return -EINVAL;
2768c2ecf20Sopenharmony_ci}
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_cistatic void dw_pcie_ep_unmap_addr(struct pci_epc *epc, u8 func_no,
2798c2ecf20Sopenharmony_ci				  phys_addr_t addr)
2808c2ecf20Sopenharmony_ci{
2818c2ecf20Sopenharmony_ci	int ret;
2828c2ecf20Sopenharmony_ci	u32 atu_index;
2838c2ecf20Sopenharmony_ci	struct dw_pcie_ep *ep = epc_get_drvdata(epc);
2848c2ecf20Sopenharmony_ci	struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	ret = dw_pcie_find_index(ep, addr, &atu_index);
2878c2ecf20Sopenharmony_ci	if (ret < 0)
2888c2ecf20Sopenharmony_ci		return;
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	dw_pcie_disable_atu(pci, atu_index, DW_PCIE_REGION_OUTBOUND);
2918c2ecf20Sopenharmony_ci	clear_bit(atu_index, ep->ob_window_map);
2928c2ecf20Sopenharmony_ci}
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_cistatic int dw_pcie_ep_map_addr(struct pci_epc *epc, u8 func_no,
2958c2ecf20Sopenharmony_ci			       phys_addr_t addr,
2968c2ecf20Sopenharmony_ci			       u64 pci_addr, size_t size)
2978c2ecf20Sopenharmony_ci{
2988c2ecf20Sopenharmony_ci	int ret;
2998c2ecf20Sopenharmony_ci	struct dw_pcie_ep *ep = epc_get_drvdata(epc);
3008c2ecf20Sopenharmony_ci	struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	ret = dw_pcie_ep_outbound_atu(ep, func_no, addr, pci_addr, size);
3038c2ecf20Sopenharmony_ci	if (ret) {
3048c2ecf20Sopenharmony_ci		dev_err(pci->dev, "Failed to enable address\n");
3058c2ecf20Sopenharmony_ci		return ret;
3068c2ecf20Sopenharmony_ci	}
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	return 0;
3098c2ecf20Sopenharmony_ci}
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_cistatic int dw_pcie_ep_get_msi(struct pci_epc *epc, u8 func_no)
3128c2ecf20Sopenharmony_ci{
3138c2ecf20Sopenharmony_ci	struct dw_pcie_ep *ep = epc_get_drvdata(epc);
3148c2ecf20Sopenharmony_ci	struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
3158c2ecf20Sopenharmony_ci	u32 val, reg;
3168c2ecf20Sopenharmony_ci	unsigned int func_offset = 0;
3178c2ecf20Sopenharmony_ci	struct dw_pcie_ep_func *ep_func;
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	ep_func = dw_pcie_ep_get_func_from_ep(ep, func_no);
3208c2ecf20Sopenharmony_ci	if (!ep_func || !ep_func->msi_cap)
3218c2ecf20Sopenharmony_ci		return -EINVAL;
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	func_offset = dw_pcie_ep_func_select(ep, func_no);
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	reg = ep_func->msi_cap + func_offset + PCI_MSI_FLAGS;
3268c2ecf20Sopenharmony_ci	val = dw_pcie_readw_dbi(pci, reg);
3278c2ecf20Sopenharmony_ci	if (!(val & PCI_MSI_FLAGS_ENABLE))
3288c2ecf20Sopenharmony_ci		return -EINVAL;
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	val = (val & PCI_MSI_FLAGS_QSIZE) >> 4;
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	return val;
3338c2ecf20Sopenharmony_ci}
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_cistatic int dw_pcie_ep_set_msi(struct pci_epc *epc, u8 func_no, u8 interrupts)
3368c2ecf20Sopenharmony_ci{
3378c2ecf20Sopenharmony_ci	struct dw_pcie_ep *ep = epc_get_drvdata(epc);
3388c2ecf20Sopenharmony_ci	struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
3398c2ecf20Sopenharmony_ci	u32 val, reg;
3408c2ecf20Sopenharmony_ci	unsigned int func_offset = 0;
3418c2ecf20Sopenharmony_ci	struct dw_pcie_ep_func *ep_func;
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	ep_func = dw_pcie_ep_get_func_from_ep(ep, func_no);
3448c2ecf20Sopenharmony_ci	if (!ep_func || !ep_func->msi_cap)
3458c2ecf20Sopenharmony_ci		return -EINVAL;
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	func_offset = dw_pcie_ep_func_select(ep, func_no);
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	reg = ep_func->msi_cap + func_offset + PCI_MSI_FLAGS;
3508c2ecf20Sopenharmony_ci	val = dw_pcie_readw_dbi(pci, reg);
3518c2ecf20Sopenharmony_ci	val &= ~PCI_MSI_FLAGS_QMASK;
3528c2ecf20Sopenharmony_ci	val |= (interrupts << 1) & PCI_MSI_FLAGS_QMASK;
3538c2ecf20Sopenharmony_ci	dw_pcie_dbi_ro_wr_en(pci);
3548c2ecf20Sopenharmony_ci	dw_pcie_writew_dbi(pci, reg, val);
3558c2ecf20Sopenharmony_ci	dw_pcie_dbi_ro_wr_dis(pci);
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	return 0;
3588c2ecf20Sopenharmony_ci}
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_cistatic int dw_pcie_ep_get_msix(struct pci_epc *epc, u8 func_no)
3618c2ecf20Sopenharmony_ci{
3628c2ecf20Sopenharmony_ci	struct dw_pcie_ep *ep = epc_get_drvdata(epc);
3638c2ecf20Sopenharmony_ci	struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
3648c2ecf20Sopenharmony_ci	u32 val, reg;
3658c2ecf20Sopenharmony_ci	unsigned int func_offset = 0;
3668c2ecf20Sopenharmony_ci	struct dw_pcie_ep_func *ep_func;
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	ep_func = dw_pcie_ep_get_func_from_ep(ep, func_no);
3698c2ecf20Sopenharmony_ci	if (!ep_func || !ep_func->msix_cap)
3708c2ecf20Sopenharmony_ci		return -EINVAL;
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	func_offset = dw_pcie_ep_func_select(ep, func_no);
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	reg = ep_func->msix_cap + func_offset + PCI_MSIX_FLAGS;
3758c2ecf20Sopenharmony_ci	val = dw_pcie_readw_dbi(pci, reg);
3768c2ecf20Sopenharmony_ci	if (!(val & PCI_MSIX_FLAGS_ENABLE))
3778c2ecf20Sopenharmony_ci		return -EINVAL;
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	val &= PCI_MSIX_FLAGS_QSIZE;
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	return val;
3828c2ecf20Sopenharmony_ci}
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_cistatic int dw_pcie_ep_set_msix(struct pci_epc *epc, u8 func_no, u16 interrupts,
3858c2ecf20Sopenharmony_ci			       enum pci_barno bir, u32 offset)
3868c2ecf20Sopenharmony_ci{
3878c2ecf20Sopenharmony_ci	struct dw_pcie_ep *ep = epc_get_drvdata(epc);
3888c2ecf20Sopenharmony_ci	struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
3898c2ecf20Sopenharmony_ci	u32 val, reg;
3908c2ecf20Sopenharmony_ci	unsigned int func_offset = 0;
3918c2ecf20Sopenharmony_ci	struct dw_pcie_ep_func *ep_func;
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci	ep_func = dw_pcie_ep_get_func_from_ep(ep, func_no);
3948c2ecf20Sopenharmony_ci	if (!ep_func || !ep_func->msix_cap)
3958c2ecf20Sopenharmony_ci		return -EINVAL;
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci	dw_pcie_dbi_ro_wr_en(pci);
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci	func_offset = dw_pcie_ep_func_select(ep, func_no);
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	reg = ep_func->msix_cap + func_offset + PCI_MSIX_FLAGS;
4028c2ecf20Sopenharmony_ci	val = dw_pcie_readw_dbi(pci, reg);
4038c2ecf20Sopenharmony_ci	val &= ~PCI_MSIX_FLAGS_QSIZE;
4048c2ecf20Sopenharmony_ci	val |= interrupts;
4058c2ecf20Sopenharmony_ci	dw_pcie_writew_dbi(pci, reg, val);
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci	reg = ep_func->msix_cap + func_offset + PCI_MSIX_TABLE;
4088c2ecf20Sopenharmony_ci	val = offset | bir;
4098c2ecf20Sopenharmony_ci	dw_pcie_writel_dbi(pci, reg, val);
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci	reg = ep_func->msix_cap + func_offset + PCI_MSIX_PBA;
4128c2ecf20Sopenharmony_ci	val = (offset + (interrupts * PCI_MSIX_ENTRY_SIZE)) | bir;
4138c2ecf20Sopenharmony_ci	dw_pcie_writel_dbi(pci, reg, val);
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	dw_pcie_dbi_ro_wr_dis(pci);
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci	return 0;
4188c2ecf20Sopenharmony_ci}
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_cistatic int dw_pcie_ep_raise_irq(struct pci_epc *epc, u8 func_no,
4218c2ecf20Sopenharmony_ci				enum pci_epc_irq_type type, u16 interrupt_num)
4228c2ecf20Sopenharmony_ci{
4238c2ecf20Sopenharmony_ci	struct dw_pcie_ep *ep = epc_get_drvdata(epc);
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	if (!ep->ops->raise_irq)
4268c2ecf20Sopenharmony_ci		return -EINVAL;
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci	return ep->ops->raise_irq(ep, func_no, type, interrupt_num);
4298c2ecf20Sopenharmony_ci}
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_cistatic void dw_pcie_ep_stop(struct pci_epc *epc)
4328c2ecf20Sopenharmony_ci{
4338c2ecf20Sopenharmony_ci	struct dw_pcie_ep *ep = epc_get_drvdata(epc);
4348c2ecf20Sopenharmony_ci	struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci	if (!pci->ops->stop_link)
4378c2ecf20Sopenharmony_ci		return;
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci	pci->ops->stop_link(pci);
4408c2ecf20Sopenharmony_ci}
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_cistatic int dw_pcie_ep_start(struct pci_epc *epc)
4438c2ecf20Sopenharmony_ci{
4448c2ecf20Sopenharmony_ci	struct dw_pcie_ep *ep = epc_get_drvdata(epc);
4458c2ecf20Sopenharmony_ci	struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci	if (!pci->ops->start_link)
4488c2ecf20Sopenharmony_ci		return -EINVAL;
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci	return pci->ops->start_link(pci);
4518c2ecf20Sopenharmony_ci}
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_cistatic const struct pci_epc_features*
4548c2ecf20Sopenharmony_cidw_pcie_ep_get_features(struct pci_epc *epc, u8 func_no)
4558c2ecf20Sopenharmony_ci{
4568c2ecf20Sopenharmony_ci	struct dw_pcie_ep *ep = epc_get_drvdata(epc);
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci	if (!ep->ops->get_features)
4598c2ecf20Sopenharmony_ci		return NULL;
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci	return ep->ops->get_features(ep);
4628c2ecf20Sopenharmony_ci}
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_cistatic const struct pci_epc_ops epc_ops = {
4658c2ecf20Sopenharmony_ci	.write_header		= dw_pcie_ep_write_header,
4668c2ecf20Sopenharmony_ci	.set_bar		= dw_pcie_ep_set_bar,
4678c2ecf20Sopenharmony_ci	.clear_bar		= dw_pcie_ep_clear_bar,
4688c2ecf20Sopenharmony_ci	.map_addr		= dw_pcie_ep_map_addr,
4698c2ecf20Sopenharmony_ci	.unmap_addr		= dw_pcie_ep_unmap_addr,
4708c2ecf20Sopenharmony_ci	.set_msi		= dw_pcie_ep_set_msi,
4718c2ecf20Sopenharmony_ci	.get_msi		= dw_pcie_ep_get_msi,
4728c2ecf20Sopenharmony_ci	.set_msix		= dw_pcie_ep_set_msix,
4738c2ecf20Sopenharmony_ci	.get_msix		= dw_pcie_ep_get_msix,
4748c2ecf20Sopenharmony_ci	.raise_irq		= dw_pcie_ep_raise_irq,
4758c2ecf20Sopenharmony_ci	.start			= dw_pcie_ep_start,
4768c2ecf20Sopenharmony_ci	.stop			= dw_pcie_ep_stop,
4778c2ecf20Sopenharmony_ci	.get_features		= dw_pcie_ep_get_features,
4788c2ecf20Sopenharmony_ci};
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ciint dw_pcie_ep_raise_legacy_irq(struct dw_pcie_ep *ep, u8 func_no)
4818c2ecf20Sopenharmony_ci{
4828c2ecf20Sopenharmony_ci	struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
4838c2ecf20Sopenharmony_ci	struct device *dev = pci->dev;
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci	dev_err(dev, "EP cannot trigger legacy IRQs\n");
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_ci	return -EINVAL;
4888c2ecf20Sopenharmony_ci}
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ciint dw_pcie_ep_raise_msi_irq(struct dw_pcie_ep *ep, u8 func_no,
4918c2ecf20Sopenharmony_ci			     u8 interrupt_num)
4928c2ecf20Sopenharmony_ci{
4938c2ecf20Sopenharmony_ci	struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
4948c2ecf20Sopenharmony_ci	struct dw_pcie_ep_func *ep_func;
4958c2ecf20Sopenharmony_ci	struct pci_epc *epc = ep->epc;
4968c2ecf20Sopenharmony_ci	unsigned int aligned_offset;
4978c2ecf20Sopenharmony_ci	unsigned int func_offset = 0;
4988c2ecf20Sopenharmony_ci	u16 msg_ctrl, msg_data;
4998c2ecf20Sopenharmony_ci	u32 msg_addr_lower, msg_addr_upper, reg;
5008c2ecf20Sopenharmony_ci	u64 msg_addr;
5018c2ecf20Sopenharmony_ci	bool has_upper;
5028c2ecf20Sopenharmony_ci	int ret;
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ci	ep_func = dw_pcie_ep_get_func_from_ep(ep, func_no);
5058c2ecf20Sopenharmony_ci	if (!ep_func || !ep_func->msi_cap)
5068c2ecf20Sopenharmony_ci		return -EINVAL;
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci	func_offset = dw_pcie_ep_func_select(ep, func_no);
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_ci	/* Raise MSI per the PCI Local Bus Specification Revision 3.0, 6.8.1. */
5118c2ecf20Sopenharmony_ci	reg = ep_func->msi_cap + func_offset + PCI_MSI_FLAGS;
5128c2ecf20Sopenharmony_ci	msg_ctrl = dw_pcie_readw_dbi(pci, reg);
5138c2ecf20Sopenharmony_ci	has_upper = !!(msg_ctrl & PCI_MSI_FLAGS_64BIT);
5148c2ecf20Sopenharmony_ci	reg = ep_func->msi_cap + func_offset + PCI_MSI_ADDRESS_LO;
5158c2ecf20Sopenharmony_ci	msg_addr_lower = dw_pcie_readl_dbi(pci, reg);
5168c2ecf20Sopenharmony_ci	if (has_upper) {
5178c2ecf20Sopenharmony_ci		reg = ep_func->msi_cap + func_offset + PCI_MSI_ADDRESS_HI;
5188c2ecf20Sopenharmony_ci		msg_addr_upper = dw_pcie_readl_dbi(pci, reg);
5198c2ecf20Sopenharmony_ci		reg = ep_func->msi_cap + func_offset + PCI_MSI_DATA_64;
5208c2ecf20Sopenharmony_ci		msg_data = dw_pcie_readw_dbi(pci, reg);
5218c2ecf20Sopenharmony_ci	} else {
5228c2ecf20Sopenharmony_ci		msg_addr_upper = 0;
5238c2ecf20Sopenharmony_ci		reg = ep_func->msi_cap + func_offset + PCI_MSI_DATA_32;
5248c2ecf20Sopenharmony_ci		msg_data = dw_pcie_readw_dbi(pci, reg);
5258c2ecf20Sopenharmony_ci	}
5268c2ecf20Sopenharmony_ci	aligned_offset = msg_addr_lower & (epc->mem->window.page_size - 1);
5278c2ecf20Sopenharmony_ci	msg_addr = ((u64)msg_addr_upper) << 32 |
5288c2ecf20Sopenharmony_ci			(msg_addr_lower & ~aligned_offset);
5298c2ecf20Sopenharmony_ci	ret = dw_pcie_ep_map_addr(epc, func_no, ep->msi_mem_phys, msg_addr,
5308c2ecf20Sopenharmony_ci				  epc->mem->window.page_size);
5318c2ecf20Sopenharmony_ci	if (ret)
5328c2ecf20Sopenharmony_ci		return ret;
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_ci	writel(msg_data | (interrupt_num - 1), ep->msi_mem + aligned_offset);
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci	dw_pcie_ep_unmap_addr(epc, func_no, ep->msi_mem_phys);
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_ci	return 0;
5398c2ecf20Sopenharmony_ci}
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_ciint dw_pcie_ep_raise_msix_irq_doorbell(struct dw_pcie_ep *ep, u8 func_no,
5428c2ecf20Sopenharmony_ci				       u16 interrupt_num)
5438c2ecf20Sopenharmony_ci{
5448c2ecf20Sopenharmony_ci	struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
5458c2ecf20Sopenharmony_ci	struct dw_pcie_ep_func *ep_func;
5468c2ecf20Sopenharmony_ci	u32 msg_data;
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci	ep_func = dw_pcie_ep_get_func_from_ep(ep, func_no);
5498c2ecf20Sopenharmony_ci	if (!ep_func || !ep_func->msix_cap)
5508c2ecf20Sopenharmony_ci		return -EINVAL;
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_ci	msg_data = (func_no << PCIE_MSIX_DOORBELL_PF_SHIFT) |
5538c2ecf20Sopenharmony_ci		   (interrupt_num - 1);
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_ci	dw_pcie_writel_dbi(pci, PCIE_MSIX_DOORBELL, msg_data);
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_ci	return 0;
5588c2ecf20Sopenharmony_ci}
5598c2ecf20Sopenharmony_ci
5608c2ecf20Sopenharmony_ciint dw_pcie_ep_raise_msix_irq(struct dw_pcie_ep *ep, u8 func_no,
5618c2ecf20Sopenharmony_ci			      u16 interrupt_num)
5628c2ecf20Sopenharmony_ci{
5638c2ecf20Sopenharmony_ci	struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
5648c2ecf20Sopenharmony_ci	struct dw_pcie_ep_func *ep_func;
5658c2ecf20Sopenharmony_ci	struct pci_epf_msix_tbl *msix_tbl;
5668c2ecf20Sopenharmony_ci	struct pci_epc *epc = ep->epc;
5678c2ecf20Sopenharmony_ci	unsigned int func_offset = 0;
5688c2ecf20Sopenharmony_ci	u32 reg, msg_data, vec_ctrl;
5698c2ecf20Sopenharmony_ci	unsigned int aligned_offset;
5708c2ecf20Sopenharmony_ci	u32 tbl_offset;
5718c2ecf20Sopenharmony_ci	u64 msg_addr;
5728c2ecf20Sopenharmony_ci	int ret;
5738c2ecf20Sopenharmony_ci	u8 bir;
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci	ep_func = dw_pcie_ep_get_func_from_ep(ep, func_no);
5768c2ecf20Sopenharmony_ci	if (!ep_func || !ep_func->msix_cap)
5778c2ecf20Sopenharmony_ci		return -EINVAL;
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_ci	func_offset = dw_pcie_ep_func_select(ep, func_no);
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_ci	reg = ep_func->msix_cap + func_offset + PCI_MSIX_TABLE;
5828c2ecf20Sopenharmony_ci	tbl_offset = dw_pcie_readl_dbi(pci, reg);
5838c2ecf20Sopenharmony_ci	bir = (tbl_offset & PCI_MSIX_TABLE_BIR);
5848c2ecf20Sopenharmony_ci	tbl_offset &= PCI_MSIX_TABLE_OFFSET;
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_ci	msix_tbl = ep->epf_bar[bir]->addr + tbl_offset;
5878c2ecf20Sopenharmony_ci	msg_addr = msix_tbl[(interrupt_num - 1)].msg_addr;
5888c2ecf20Sopenharmony_ci	msg_data = msix_tbl[(interrupt_num - 1)].msg_data;
5898c2ecf20Sopenharmony_ci	vec_ctrl = msix_tbl[(interrupt_num - 1)].vector_ctrl;
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_ci	if (vec_ctrl & PCI_MSIX_ENTRY_CTRL_MASKBIT) {
5928c2ecf20Sopenharmony_ci		dev_dbg(pci->dev, "MSI-X entry ctrl set\n");
5938c2ecf20Sopenharmony_ci		return -EPERM;
5948c2ecf20Sopenharmony_ci	}
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_ci	aligned_offset = msg_addr & (epc->mem->window.page_size - 1);
5978c2ecf20Sopenharmony_ci	msg_addr = ALIGN_DOWN(msg_addr, epc->mem->window.page_size);
5988c2ecf20Sopenharmony_ci	ret = dw_pcie_ep_map_addr(epc, func_no, ep->msi_mem_phys,  msg_addr,
5998c2ecf20Sopenharmony_ci				  epc->mem->window.page_size);
6008c2ecf20Sopenharmony_ci	if (ret)
6018c2ecf20Sopenharmony_ci		return ret;
6028c2ecf20Sopenharmony_ci
6038c2ecf20Sopenharmony_ci	writel(msg_data, ep->msi_mem + aligned_offset);
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_ci	dw_pcie_ep_unmap_addr(epc, func_no, ep->msi_mem_phys);
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_ci	return 0;
6088c2ecf20Sopenharmony_ci}
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_civoid dw_pcie_ep_exit(struct dw_pcie_ep *ep)
6118c2ecf20Sopenharmony_ci{
6128c2ecf20Sopenharmony_ci	struct pci_epc *epc = ep->epc;
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_ci	pci_epc_mem_free_addr(epc, ep->msi_mem_phys, ep->msi_mem,
6158c2ecf20Sopenharmony_ci			      epc->mem->window.page_size);
6168c2ecf20Sopenharmony_ci
6178c2ecf20Sopenharmony_ci	pci_epc_mem_exit(epc);
6188c2ecf20Sopenharmony_ci}
6198c2ecf20Sopenharmony_ci
6208c2ecf20Sopenharmony_cistatic unsigned int dw_pcie_ep_find_ext_capability(struct dw_pcie *pci, int cap)
6218c2ecf20Sopenharmony_ci{
6228c2ecf20Sopenharmony_ci	u32 header;
6238c2ecf20Sopenharmony_ci	int pos = PCI_CFG_SPACE_SIZE;
6248c2ecf20Sopenharmony_ci
6258c2ecf20Sopenharmony_ci	while (pos) {
6268c2ecf20Sopenharmony_ci		header = dw_pcie_readl_dbi(pci, pos);
6278c2ecf20Sopenharmony_ci		if (PCI_EXT_CAP_ID(header) == cap)
6288c2ecf20Sopenharmony_ci			return pos;
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_ci		pos = PCI_EXT_CAP_NEXT(header);
6318c2ecf20Sopenharmony_ci		if (!pos)
6328c2ecf20Sopenharmony_ci			break;
6338c2ecf20Sopenharmony_ci	}
6348c2ecf20Sopenharmony_ci
6358c2ecf20Sopenharmony_ci	return 0;
6368c2ecf20Sopenharmony_ci}
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_ciint dw_pcie_ep_init_complete(struct dw_pcie_ep *ep)
6398c2ecf20Sopenharmony_ci{
6408c2ecf20Sopenharmony_ci	struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
6418c2ecf20Sopenharmony_ci	unsigned int offset;
6428c2ecf20Sopenharmony_ci	unsigned int nbars;
6438c2ecf20Sopenharmony_ci	u8 hdr_type;
6448c2ecf20Sopenharmony_ci	u32 reg;
6458c2ecf20Sopenharmony_ci	int i;
6468c2ecf20Sopenharmony_ci
6478c2ecf20Sopenharmony_ci	hdr_type = dw_pcie_readb_dbi(pci, PCI_HEADER_TYPE) &
6488c2ecf20Sopenharmony_ci		   PCI_HEADER_TYPE_MASK;
6498c2ecf20Sopenharmony_ci	if (hdr_type != PCI_HEADER_TYPE_NORMAL) {
6508c2ecf20Sopenharmony_ci		dev_err(pci->dev,
6518c2ecf20Sopenharmony_ci			"PCIe controller is not set to EP mode (hdr_type:0x%x)!\n",
6528c2ecf20Sopenharmony_ci			hdr_type);
6538c2ecf20Sopenharmony_ci		return -EIO;
6548c2ecf20Sopenharmony_ci	}
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ci	offset = dw_pcie_ep_find_ext_capability(pci, PCI_EXT_CAP_ID_REBAR);
6578c2ecf20Sopenharmony_ci
6588c2ecf20Sopenharmony_ci	dw_pcie_dbi_ro_wr_en(pci);
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_ci	if (offset) {
6618c2ecf20Sopenharmony_ci		reg = dw_pcie_readl_dbi(pci, offset + PCI_REBAR_CTRL);
6628c2ecf20Sopenharmony_ci		nbars = (reg & PCI_REBAR_CTRL_NBAR_MASK) >>
6638c2ecf20Sopenharmony_ci			PCI_REBAR_CTRL_NBAR_SHIFT;
6648c2ecf20Sopenharmony_ci
6658c2ecf20Sopenharmony_ci		for (i = 0; i < nbars; i++, offset += PCI_REBAR_CTRL)
6668c2ecf20Sopenharmony_ci			dw_pcie_writel_dbi(pci, offset + PCI_REBAR_CAP, 0x0);
6678c2ecf20Sopenharmony_ci	}
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_ci	dw_pcie_setup(pci);
6708c2ecf20Sopenharmony_ci	dw_pcie_dbi_ro_wr_dis(pci);
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_ci	return 0;
6738c2ecf20Sopenharmony_ci}
6748c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(dw_pcie_ep_init_complete);
6758c2ecf20Sopenharmony_ci
6768c2ecf20Sopenharmony_ciint dw_pcie_ep_init(struct dw_pcie_ep *ep)
6778c2ecf20Sopenharmony_ci{
6788c2ecf20Sopenharmony_ci	int ret;
6798c2ecf20Sopenharmony_ci	void *addr;
6808c2ecf20Sopenharmony_ci	u8 func_no;
6818c2ecf20Sopenharmony_ci	struct pci_epc *epc;
6828c2ecf20Sopenharmony_ci	struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
6838c2ecf20Sopenharmony_ci	struct device *dev = pci->dev;
6848c2ecf20Sopenharmony_ci	struct device_node *np = dev->of_node;
6858c2ecf20Sopenharmony_ci	const struct pci_epc_features *epc_features;
6868c2ecf20Sopenharmony_ci	struct dw_pcie_ep_func *ep_func;
6878c2ecf20Sopenharmony_ci
6888c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&ep->func_list);
6898c2ecf20Sopenharmony_ci
6908c2ecf20Sopenharmony_ci	if (!pci->dbi_base || !pci->dbi_base2) {
6918c2ecf20Sopenharmony_ci		dev_err(dev, "dbi_base/dbi_base2 is not populated\n");
6928c2ecf20Sopenharmony_ci		return -EINVAL;
6938c2ecf20Sopenharmony_ci	}
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_ci	ret = of_property_read_u32(np, "num-ib-windows", &ep->num_ib_windows);
6968c2ecf20Sopenharmony_ci	if (ret < 0) {
6978c2ecf20Sopenharmony_ci		dev_err(dev, "Unable to read *num-ib-windows* property\n");
6988c2ecf20Sopenharmony_ci		return ret;
6998c2ecf20Sopenharmony_ci	}
7008c2ecf20Sopenharmony_ci	if (ep->num_ib_windows > MAX_IATU_IN) {
7018c2ecf20Sopenharmony_ci		dev_err(dev, "Invalid *num-ib-windows*\n");
7028c2ecf20Sopenharmony_ci		return -EINVAL;
7038c2ecf20Sopenharmony_ci	}
7048c2ecf20Sopenharmony_ci
7058c2ecf20Sopenharmony_ci	ret = of_property_read_u32(np, "num-ob-windows", &ep->num_ob_windows);
7068c2ecf20Sopenharmony_ci	if (ret < 0) {
7078c2ecf20Sopenharmony_ci		dev_err(dev, "Unable to read *num-ob-windows* property\n");
7088c2ecf20Sopenharmony_ci		return ret;
7098c2ecf20Sopenharmony_ci	}
7108c2ecf20Sopenharmony_ci	if (ep->num_ob_windows > MAX_IATU_OUT) {
7118c2ecf20Sopenharmony_ci		dev_err(dev, "Invalid *num-ob-windows*\n");
7128c2ecf20Sopenharmony_ci		return -EINVAL;
7138c2ecf20Sopenharmony_ci	}
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_ci	ep->ib_window_map = devm_kcalloc(dev,
7168c2ecf20Sopenharmony_ci					 BITS_TO_LONGS(ep->num_ib_windows),
7178c2ecf20Sopenharmony_ci					 sizeof(long),
7188c2ecf20Sopenharmony_ci					 GFP_KERNEL);
7198c2ecf20Sopenharmony_ci	if (!ep->ib_window_map)
7208c2ecf20Sopenharmony_ci		return -ENOMEM;
7218c2ecf20Sopenharmony_ci
7228c2ecf20Sopenharmony_ci	ep->ob_window_map = devm_kcalloc(dev,
7238c2ecf20Sopenharmony_ci					 BITS_TO_LONGS(ep->num_ob_windows),
7248c2ecf20Sopenharmony_ci					 sizeof(long),
7258c2ecf20Sopenharmony_ci					 GFP_KERNEL);
7268c2ecf20Sopenharmony_ci	if (!ep->ob_window_map)
7278c2ecf20Sopenharmony_ci		return -ENOMEM;
7288c2ecf20Sopenharmony_ci
7298c2ecf20Sopenharmony_ci	addr = devm_kcalloc(dev, ep->num_ob_windows, sizeof(phys_addr_t),
7308c2ecf20Sopenharmony_ci			    GFP_KERNEL);
7318c2ecf20Sopenharmony_ci	if (!addr)
7328c2ecf20Sopenharmony_ci		return -ENOMEM;
7338c2ecf20Sopenharmony_ci	ep->outbound_addr = addr;
7348c2ecf20Sopenharmony_ci
7358c2ecf20Sopenharmony_ci	if (pci->link_gen < 1)
7368c2ecf20Sopenharmony_ci		pci->link_gen = of_pci_get_max_link_speed(np);
7378c2ecf20Sopenharmony_ci
7388c2ecf20Sopenharmony_ci	epc = devm_pci_epc_create(dev, &epc_ops);
7398c2ecf20Sopenharmony_ci	if (IS_ERR(epc)) {
7408c2ecf20Sopenharmony_ci		dev_err(dev, "Failed to create epc device\n");
7418c2ecf20Sopenharmony_ci		return PTR_ERR(epc);
7428c2ecf20Sopenharmony_ci	}
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_ci	ep->epc = epc;
7458c2ecf20Sopenharmony_ci	epc_set_drvdata(epc, ep);
7468c2ecf20Sopenharmony_ci
7478c2ecf20Sopenharmony_ci	ret = of_property_read_u8(np, "max-functions", &epc->max_functions);
7488c2ecf20Sopenharmony_ci	if (ret < 0)
7498c2ecf20Sopenharmony_ci		epc->max_functions = 1;
7508c2ecf20Sopenharmony_ci
7518c2ecf20Sopenharmony_ci	for (func_no = 0; func_no < epc->max_functions; func_no++) {
7528c2ecf20Sopenharmony_ci		ep_func = devm_kzalloc(dev, sizeof(*ep_func), GFP_KERNEL);
7538c2ecf20Sopenharmony_ci		if (!ep_func)
7548c2ecf20Sopenharmony_ci			return -ENOMEM;
7558c2ecf20Sopenharmony_ci
7568c2ecf20Sopenharmony_ci		ep_func->func_no = func_no;
7578c2ecf20Sopenharmony_ci		ep_func->msi_cap = dw_pcie_ep_find_capability(ep, func_no,
7588c2ecf20Sopenharmony_ci							      PCI_CAP_ID_MSI);
7598c2ecf20Sopenharmony_ci		ep_func->msix_cap = dw_pcie_ep_find_capability(ep, func_no,
7608c2ecf20Sopenharmony_ci							       PCI_CAP_ID_MSIX);
7618c2ecf20Sopenharmony_ci
7628c2ecf20Sopenharmony_ci		list_add_tail(&ep_func->list, &ep->func_list);
7638c2ecf20Sopenharmony_ci	}
7648c2ecf20Sopenharmony_ci
7658c2ecf20Sopenharmony_ci	if (ep->ops->ep_init)
7668c2ecf20Sopenharmony_ci		ep->ops->ep_init(ep);
7678c2ecf20Sopenharmony_ci
7688c2ecf20Sopenharmony_ci	ret = pci_epc_mem_init(epc, ep->phys_base, ep->addr_size,
7698c2ecf20Sopenharmony_ci			       ep->page_size);
7708c2ecf20Sopenharmony_ci	if (ret < 0) {
7718c2ecf20Sopenharmony_ci		dev_err(dev, "Failed to initialize address space\n");
7728c2ecf20Sopenharmony_ci		return ret;
7738c2ecf20Sopenharmony_ci	}
7748c2ecf20Sopenharmony_ci
7758c2ecf20Sopenharmony_ci	ep->msi_mem = pci_epc_mem_alloc_addr(epc, &ep->msi_mem_phys,
7768c2ecf20Sopenharmony_ci					     epc->mem->window.page_size);
7778c2ecf20Sopenharmony_ci	if (!ep->msi_mem) {
7788c2ecf20Sopenharmony_ci		ret = -ENOMEM;
7798c2ecf20Sopenharmony_ci		dev_err(dev, "Failed to reserve memory for MSI/MSI-X\n");
7808c2ecf20Sopenharmony_ci		goto err_exit_epc_mem;
7818c2ecf20Sopenharmony_ci	}
7828c2ecf20Sopenharmony_ci
7838c2ecf20Sopenharmony_ci	if (ep->ops->get_features) {
7848c2ecf20Sopenharmony_ci		epc_features = ep->ops->get_features(ep);
7858c2ecf20Sopenharmony_ci		if (epc_features->core_init_notifier)
7868c2ecf20Sopenharmony_ci			return 0;
7878c2ecf20Sopenharmony_ci	}
7888c2ecf20Sopenharmony_ci
7898c2ecf20Sopenharmony_ci	ret = dw_pcie_ep_init_complete(ep);
7908c2ecf20Sopenharmony_ci	if (ret)
7918c2ecf20Sopenharmony_ci		goto err_free_epc_mem;
7928c2ecf20Sopenharmony_ci
7938c2ecf20Sopenharmony_ci	return 0;
7948c2ecf20Sopenharmony_ci
7958c2ecf20Sopenharmony_cierr_free_epc_mem:
7968c2ecf20Sopenharmony_ci	pci_epc_mem_free_addr(epc, ep->msi_mem_phys, ep->msi_mem,
7978c2ecf20Sopenharmony_ci			      epc->mem->window.page_size);
7988c2ecf20Sopenharmony_ci
7998c2ecf20Sopenharmony_cierr_exit_epc_mem:
8008c2ecf20Sopenharmony_ci	pci_epc_mem_exit(epc);
8018c2ecf20Sopenharmony_ci
8028c2ecf20Sopenharmony_ci	return ret;
8038c2ecf20Sopenharmony_ci}
8048c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(dw_pcie_ep_init);
805