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