mirror of
https://github.com/Qortal/Brooklyn.git
synced 2025-02-07 23:03:06 +00:00
Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey!
565 lines
14 KiB
C
565 lines
14 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* PCIe endpoint driver for Renesas R-Car SoCs
|
|
* Copyright (c) 2020 Renesas Electronics Europe GmbH
|
|
*
|
|
* Author: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
|
|
*/
|
|
|
|
#include <linux/clk.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/of_address.h>
|
|
#include <linux/of_irq.h>
|
|
#include <linux/of_pci.h>
|
|
#include <linux/of_platform.h>
|
|
#include <linux/pci.h>
|
|
#include <linux/pci-epc.h>
|
|
#include <linux/phy/phy.h>
|
|
#include <linux/platform_device.h>
|
|
|
|
#include "pcie-rcar.h"
|
|
|
|
#define RCAR_EPC_MAX_FUNCTIONS 1
|
|
|
|
/* Structure representing the PCIe interface */
|
|
struct rcar_pcie_endpoint {
|
|
struct rcar_pcie pcie;
|
|
phys_addr_t *ob_mapped_addr;
|
|
struct pci_epc_mem_window *ob_window;
|
|
u8 max_functions;
|
|
unsigned int bar_to_atu[MAX_NR_INBOUND_MAPS];
|
|
unsigned long *ib_window_map;
|
|
u32 num_ib_windows;
|
|
u32 num_ob_windows;
|
|
};
|
|
|
|
static void rcar_pcie_ep_hw_init(struct rcar_pcie *pcie)
|
|
{
|
|
u32 val;
|
|
|
|
rcar_pci_write_reg(pcie, 0, PCIETCTLR);
|
|
|
|
/* Set endpoint mode */
|
|
rcar_pci_write_reg(pcie, 0, PCIEMSR);
|
|
|
|
/* Initialize default capabilities. */
|
|
rcar_rmw32(pcie, REXPCAP(0), 0xff, PCI_CAP_ID_EXP);
|
|
rcar_rmw32(pcie, REXPCAP(PCI_EXP_FLAGS),
|
|
PCI_EXP_FLAGS_TYPE, PCI_EXP_TYPE_ENDPOINT << 4);
|
|
rcar_rmw32(pcie, RCONF(PCI_HEADER_TYPE), 0x7f,
|
|
PCI_HEADER_TYPE_NORMAL);
|
|
|
|
/* Write out the physical slot number = 0 */
|
|
rcar_rmw32(pcie, REXPCAP(PCI_EXP_SLTCAP), PCI_EXP_SLTCAP_PSN, 0);
|
|
|
|
val = rcar_pci_read_reg(pcie, EXPCAP(1));
|
|
/* device supports fixed 128 bytes MPSS */
|
|
val &= ~GENMASK(2, 0);
|
|
rcar_pci_write_reg(pcie, val, EXPCAP(1));
|
|
|
|
val = rcar_pci_read_reg(pcie, EXPCAP(2));
|
|
/* read requests size 128 bytes */
|
|
val &= ~GENMASK(14, 12);
|
|
/* payload size 128 bytes */
|
|
val &= ~GENMASK(7, 5);
|
|
rcar_pci_write_reg(pcie, val, EXPCAP(2));
|
|
|
|
/* Set target link speed to 5.0 GT/s */
|
|
rcar_rmw32(pcie, EXPCAP(12), PCI_EXP_LNKSTA_CLS,
|
|
PCI_EXP_LNKSTA_CLS_5_0GB);
|
|
|
|
/* Set the completion timer timeout to the maximum 50ms. */
|
|
rcar_rmw32(pcie, TLCTLR + 1, 0x3f, 50);
|
|
|
|
/* Terminate list of capabilities (Next Capability Offset=0) */
|
|
rcar_rmw32(pcie, RVCCAP(0), 0xfff00000, 0);
|
|
|
|
/* flush modifications */
|
|
wmb();
|
|
}
|
|
|
|
static int rcar_pcie_ep_get_window(struct rcar_pcie_endpoint *ep,
|
|
phys_addr_t addr)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < ep->num_ob_windows; i++)
|
|
if (ep->ob_window[i].phys_base == addr)
|
|
return i;
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
static int rcar_pcie_parse_outbound_ranges(struct rcar_pcie_endpoint *ep,
|
|
struct platform_device *pdev)
|
|
{
|
|
struct rcar_pcie *pcie = &ep->pcie;
|
|
char outbound_name[10];
|
|
struct resource *res;
|
|
unsigned int i = 0;
|
|
|
|
ep->num_ob_windows = 0;
|
|
for (i = 0; i < RCAR_PCI_MAX_RESOURCES; i++) {
|
|
sprintf(outbound_name, "memory%u", i);
|
|
res = platform_get_resource_byname(pdev,
|
|
IORESOURCE_MEM,
|
|
outbound_name);
|
|
if (!res) {
|
|
dev_err(pcie->dev, "missing outbound window %u\n", i);
|
|
return -EINVAL;
|
|
}
|
|
if (!devm_request_mem_region(&pdev->dev, res->start,
|
|
resource_size(res),
|
|
outbound_name)) {
|
|
dev_err(pcie->dev, "Cannot request memory region %s.\n",
|
|
outbound_name);
|
|
return -EIO;
|
|
}
|
|
|
|
ep->ob_window[i].phys_base = res->start;
|
|
ep->ob_window[i].size = resource_size(res);
|
|
/* controller doesn't support multiple allocation
|
|
* from same window, so set page_size to window size
|
|
*/
|
|
ep->ob_window[i].page_size = resource_size(res);
|
|
}
|
|
ep->num_ob_windows = i;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int rcar_pcie_ep_get_pdata(struct rcar_pcie_endpoint *ep,
|
|
struct platform_device *pdev)
|
|
{
|
|
struct rcar_pcie *pcie = &ep->pcie;
|
|
struct pci_epc_mem_window *window;
|
|
struct device *dev = pcie->dev;
|
|
struct resource res;
|
|
int err;
|
|
|
|
err = of_address_to_resource(dev->of_node, 0, &res);
|
|
if (err)
|
|
return err;
|
|
pcie->base = devm_ioremap_resource(dev, &res);
|
|
if (IS_ERR(pcie->base))
|
|
return PTR_ERR(pcie->base);
|
|
|
|
ep->ob_window = devm_kcalloc(dev, RCAR_PCI_MAX_RESOURCES,
|
|
sizeof(*window), GFP_KERNEL);
|
|
if (!ep->ob_window)
|
|
return -ENOMEM;
|
|
|
|
rcar_pcie_parse_outbound_ranges(ep, pdev);
|
|
|
|
err = of_property_read_u8(dev->of_node, "max-functions",
|
|
&ep->max_functions);
|
|
if (err < 0 || ep->max_functions > RCAR_EPC_MAX_FUNCTIONS)
|
|
ep->max_functions = RCAR_EPC_MAX_FUNCTIONS;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int rcar_pcie_ep_write_header(struct pci_epc *epc, u8 fn, u8 vfn,
|
|
struct pci_epf_header *hdr)
|
|
{
|
|
struct rcar_pcie_endpoint *ep = epc_get_drvdata(epc);
|
|
struct rcar_pcie *pcie = &ep->pcie;
|
|
u32 val;
|
|
|
|
if (!fn)
|
|
val = hdr->vendorid;
|
|
else
|
|
val = rcar_pci_read_reg(pcie, IDSETR0);
|
|
val |= hdr->deviceid << 16;
|
|
rcar_pci_write_reg(pcie, val, IDSETR0);
|
|
|
|
val = hdr->revid;
|
|
val |= hdr->progif_code << 8;
|
|
val |= hdr->subclass_code << 16;
|
|
val |= hdr->baseclass_code << 24;
|
|
rcar_pci_write_reg(pcie, val, IDSETR1);
|
|
|
|
if (!fn)
|
|
val = hdr->subsys_vendor_id;
|
|
else
|
|
val = rcar_pci_read_reg(pcie, SUBIDSETR);
|
|
val |= hdr->subsys_id << 16;
|
|
rcar_pci_write_reg(pcie, val, SUBIDSETR);
|
|
|
|
if (hdr->interrupt_pin > PCI_INTERRUPT_INTA)
|
|
return -EINVAL;
|
|
val = rcar_pci_read_reg(pcie, PCICONF(15));
|
|
val |= (hdr->interrupt_pin << 8);
|
|
rcar_pci_write_reg(pcie, val, PCICONF(15));
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int rcar_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
|
|
struct pci_epf_bar *epf_bar)
|
|
{
|
|
int flags = epf_bar->flags | LAR_ENABLE | LAM_64BIT;
|
|
struct rcar_pcie_endpoint *ep = epc_get_drvdata(epc);
|
|
u64 size = 1ULL << fls64(epf_bar->size - 1);
|
|
dma_addr_t cpu_addr = epf_bar->phys_addr;
|
|
enum pci_barno bar = epf_bar->barno;
|
|
struct rcar_pcie *pcie = &ep->pcie;
|
|
u32 mask;
|
|
int idx;
|
|
int err;
|
|
|
|
idx = find_first_zero_bit(ep->ib_window_map, ep->num_ib_windows);
|
|
if (idx >= ep->num_ib_windows) {
|
|
dev_err(pcie->dev, "no free inbound window\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if ((flags & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO)
|
|
flags |= IO_SPACE;
|
|
|
|
ep->bar_to_atu[bar] = idx;
|
|
/* use 64-bit BARs */
|
|
set_bit(idx, ep->ib_window_map);
|
|
set_bit(idx + 1, ep->ib_window_map);
|
|
|
|
if (cpu_addr > 0) {
|
|
unsigned long nr_zeros = __ffs64(cpu_addr);
|
|
u64 alignment = 1ULL << nr_zeros;
|
|
|
|
size = min(size, alignment);
|
|
}
|
|
|
|
size = min(size, 1ULL << 32);
|
|
|
|
mask = roundup_pow_of_two(size) - 1;
|
|
mask &= ~0xf;
|
|
|
|
rcar_pcie_set_inbound(pcie, cpu_addr,
|
|
0x0, mask | flags, idx, false);
|
|
|
|
err = rcar_pcie_wait_for_phyrdy(pcie);
|
|
if (err) {
|
|
dev_err(pcie->dev, "phy not ready\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void rcar_pcie_ep_clear_bar(struct pci_epc *epc, u8 fn, u8 vfn,
|
|
struct pci_epf_bar *epf_bar)
|
|
{
|
|
struct rcar_pcie_endpoint *ep = epc_get_drvdata(epc);
|
|
enum pci_barno bar = epf_bar->barno;
|
|
u32 atu_index = ep->bar_to_atu[bar];
|
|
|
|
rcar_pcie_set_inbound(&ep->pcie, 0x0, 0x0, 0x0, bar, false);
|
|
|
|
clear_bit(atu_index, ep->ib_window_map);
|
|
clear_bit(atu_index + 1, ep->ib_window_map);
|
|
}
|
|
|
|
static int rcar_pcie_ep_set_msi(struct pci_epc *epc, u8 fn, u8 vfn,
|
|
u8 interrupts)
|
|
{
|
|
struct rcar_pcie_endpoint *ep = epc_get_drvdata(epc);
|
|
struct rcar_pcie *pcie = &ep->pcie;
|
|
u32 flags;
|
|
|
|
flags = rcar_pci_read_reg(pcie, MSICAP(fn));
|
|
flags |= interrupts << MSICAP0_MMESCAP_OFFSET;
|
|
rcar_pci_write_reg(pcie, flags, MSICAP(fn));
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int rcar_pcie_ep_get_msi(struct pci_epc *epc, u8 fn, u8 vfn)
|
|
{
|
|
struct rcar_pcie_endpoint *ep = epc_get_drvdata(epc);
|
|
struct rcar_pcie *pcie = &ep->pcie;
|
|
u32 flags;
|
|
|
|
flags = rcar_pci_read_reg(pcie, MSICAP(fn));
|
|
if (!(flags & MSICAP0_MSIE))
|
|
return -EINVAL;
|
|
|
|
return ((flags & MSICAP0_MMESE_MASK) >> MSICAP0_MMESE_OFFSET);
|
|
}
|
|
|
|
static int rcar_pcie_ep_map_addr(struct pci_epc *epc, u8 fn, u8 vfn,
|
|
phys_addr_t addr, u64 pci_addr, size_t size)
|
|
{
|
|
struct rcar_pcie_endpoint *ep = epc_get_drvdata(epc);
|
|
struct rcar_pcie *pcie = &ep->pcie;
|
|
struct resource_entry win;
|
|
struct resource res;
|
|
int window;
|
|
int err;
|
|
|
|
/* check if we have a link. */
|
|
err = rcar_pcie_wait_for_dl(pcie);
|
|
if (err) {
|
|
dev_err(pcie->dev, "link not up\n");
|
|
return err;
|
|
}
|
|
|
|
window = rcar_pcie_ep_get_window(ep, addr);
|
|
if (window < 0) {
|
|
dev_err(pcie->dev, "failed to get corresponding window\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
memset(&win, 0x0, sizeof(win));
|
|
memset(&res, 0x0, sizeof(res));
|
|
res.start = pci_addr;
|
|
res.end = pci_addr + size - 1;
|
|
res.flags = IORESOURCE_MEM;
|
|
win.res = &res;
|
|
|
|
rcar_pcie_set_outbound(pcie, window, &win);
|
|
|
|
ep->ob_mapped_addr[window] = addr;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void rcar_pcie_ep_unmap_addr(struct pci_epc *epc, u8 fn, u8 vfn,
|
|
phys_addr_t addr)
|
|
{
|
|
struct rcar_pcie_endpoint *ep = epc_get_drvdata(epc);
|
|
struct resource_entry win;
|
|
struct resource res;
|
|
int idx;
|
|
|
|
for (idx = 0; idx < ep->num_ob_windows; idx++)
|
|
if (ep->ob_mapped_addr[idx] == addr)
|
|
break;
|
|
|
|
if (idx >= ep->num_ob_windows)
|
|
return;
|
|
|
|
memset(&win, 0x0, sizeof(win));
|
|
memset(&res, 0x0, sizeof(res));
|
|
win.res = &res;
|
|
rcar_pcie_set_outbound(&ep->pcie, idx, &win);
|
|
|
|
ep->ob_mapped_addr[idx] = 0;
|
|
}
|
|
|
|
static int rcar_pcie_ep_assert_intx(struct rcar_pcie_endpoint *ep,
|
|
u8 fn, u8 intx)
|
|
{
|
|
struct rcar_pcie *pcie = &ep->pcie;
|
|
u32 val;
|
|
|
|
val = rcar_pci_read_reg(pcie, PCIEMSITXR);
|
|
if ((val & PCI_MSI_FLAGS_ENABLE)) {
|
|
dev_err(pcie->dev, "MSI is enabled, cannot assert INTx\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
val = rcar_pci_read_reg(pcie, PCICONF(1));
|
|
if ((val & INTDIS)) {
|
|
dev_err(pcie->dev, "INTx message transmission is disabled\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
val = rcar_pci_read_reg(pcie, PCIEINTXR);
|
|
if ((val & ASTINTX)) {
|
|
dev_err(pcie->dev, "INTx is already asserted\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
val |= ASTINTX;
|
|
rcar_pci_write_reg(pcie, val, PCIEINTXR);
|
|
usleep_range(1000, 1001);
|
|
val = rcar_pci_read_reg(pcie, PCIEINTXR);
|
|
val &= ~ASTINTX;
|
|
rcar_pci_write_reg(pcie, val, PCIEINTXR);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int rcar_pcie_ep_assert_msi(struct rcar_pcie *pcie,
|
|
u8 fn, u8 interrupt_num)
|
|
{
|
|
u16 msi_count;
|
|
u32 val;
|
|
|
|
/* Check MSI enable bit */
|
|
val = rcar_pci_read_reg(pcie, MSICAP(fn));
|
|
if (!(val & MSICAP0_MSIE))
|
|
return -EINVAL;
|
|
|
|
/* Get MSI numbers from MME */
|
|
msi_count = ((val & MSICAP0_MMESE_MASK) >> MSICAP0_MMESE_OFFSET);
|
|
msi_count = 1 << msi_count;
|
|
|
|
if (!interrupt_num || interrupt_num > msi_count)
|
|
return -EINVAL;
|
|
|
|
val = rcar_pci_read_reg(pcie, PCIEMSITXR);
|
|
rcar_pci_write_reg(pcie, val | (interrupt_num - 1), PCIEMSITXR);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int rcar_pcie_ep_raise_irq(struct pci_epc *epc, u8 fn, u8 vfn,
|
|
enum pci_epc_irq_type type,
|
|
u16 interrupt_num)
|
|
{
|
|
struct rcar_pcie_endpoint *ep = epc_get_drvdata(epc);
|
|
|
|
switch (type) {
|
|
case PCI_EPC_IRQ_LEGACY:
|
|
return rcar_pcie_ep_assert_intx(ep, fn, 0);
|
|
|
|
case PCI_EPC_IRQ_MSI:
|
|
return rcar_pcie_ep_assert_msi(&ep->pcie, fn, interrupt_num);
|
|
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
static int rcar_pcie_ep_start(struct pci_epc *epc)
|
|
{
|
|
struct rcar_pcie_endpoint *ep = epc_get_drvdata(epc);
|
|
|
|
rcar_pci_write_reg(&ep->pcie, MACCTLR_INIT_VAL, MACCTLR);
|
|
rcar_pci_write_reg(&ep->pcie, CFINIT, PCIETCTLR);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void rcar_pcie_ep_stop(struct pci_epc *epc)
|
|
{
|
|
struct rcar_pcie_endpoint *ep = epc_get_drvdata(epc);
|
|
|
|
rcar_pci_write_reg(&ep->pcie, 0, PCIETCTLR);
|
|
}
|
|
|
|
static const struct pci_epc_features rcar_pcie_epc_features = {
|
|
.linkup_notifier = false,
|
|
.msi_capable = true,
|
|
.msix_capable = false,
|
|
/* use 64-bit BARs so mark BAR[1,3,5] as reserved */
|
|
.reserved_bar = 1 << BAR_1 | 1 << BAR_3 | 1 << BAR_5,
|
|
.bar_fixed_64bit = 1 << BAR_0 | 1 << BAR_2 | 1 << BAR_4,
|
|
.bar_fixed_size[0] = 128,
|
|
.bar_fixed_size[2] = 256,
|
|
.bar_fixed_size[4] = 256,
|
|
};
|
|
|
|
static const struct pci_epc_features*
|
|
rcar_pcie_ep_get_features(struct pci_epc *epc, u8 func_no, u8 vfunc_no)
|
|
{
|
|
return &rcar_pcie_epc_features;
|
|
}
|
|
|
|
static const struct pci_epc_ops rcar_pcie_epc_ops = {
|
|
.write_header = rcar_pcie_ep_write_header,
|
|
.set_bar = rcar_pcie_ep_set_bar,
|
|
.clear_bar = rcar_pcie_ep_clear_bar,
|
|
.set_msi = rcar_pcie_ep_set_msi,
|
|
.get_msi = rcar_pcie_ep_get_msi,
|
|
.map_addr = rcar_pcie_ep_map_addr,
|
|
.unmap_addr = rcar_pcie_ep_unmap_addr,
|
|
.raise_irq = rcar_pcie_ep_raise_irq,
|
|
.start = rcar_pcie_ep_start,
|
|
.stop = rcar_pcie_ep_stop,
|
|
.get_features = rcar_pcie_ep_get_features,
|
|
};
|
|
|
|
static const struct of_device_id rcar_pcie_ep_of_match[] = {
|
|
{ .compatible = "renesas,r8a774c0-pcie-ep", },
|
|
{ .compatible = "renesas,rcar-gen3-pcie-ep" },
|
|
{ },
|
|
};
|
|
|
|
static int rcar_pcie_ep_probe(struct platform_device *pdev)
|
|
{
|
|
struct device *dev = &pdev->dev;
|
|
struct rcar_pcie_endpoint *ep;
|
|
struct rcar_pcie *pcie;
|
|
struct pci_epc *epc;
|
|
int err;
|
|
|
|
ep = devm_kzalloc(dev, sizeof(*ep), GFP_KERNEL);
|
|
if (!ep)
|
|
return -ENOMEM;
|
|
|
|
pcie = &ep->pcie;
|
|
pcie->dev = dev;
|
|
|
|
pm_runtime_enable(dev);
|
|
err = pm_runtime_resume_and_get(dev);
|
|
if (err < 0) {
|
|
dev_err(dev, "pm_runtime_resume_and_get failed\n");
|
|
goto err_pm_disable;
|
|
}
|
|
|
|
err = rcar_pcie_ep_get_pdata(ep, pdev);
|
|
if (err < 0) {
|
|
dev_err(dev, "failed to request resources: %d\n", err);
|
|
goto err_pm_put;
|
|
}
|
|
|
|
ep->num_ib_windows = MAX_NR_INBOUND_MAPS;
|
|
ep->ib_window_map =
|
|
devm_kcalloc(dev, BITS_TO_LONGS(ep->num_ib_windows),
|
|
sizeof(long), GFP_KERNEL);
|
|
if (!ep->ib_window_map) {
|
|
err = -ENOMEM;
|
|
dev_err(dev, "failed to allocate memory for inbound map\n");
|
|
goto err_pm_put;
|
|
}
|
|
|
|
ep->ob_mapped_addr = devm_kcalloc(dev, ep->num_ob_windows,
|
|
sizeof(*ep->ob_mapped_addr),
|
|
GFP_KERNEL);
|
|
if (!ep->ob_mapped_addr) {
|
|
err = -ENOMEM;
|
|
dev_err(dev, "failed to allocate memory for outbound memory pointers\n");
|
|
goto err_pm_put;
|
|
}
|
|
|
|
epc = devm_pci_epc_create(dev, &rcar_pcie_epc_ops);
|
|
if (IS_ERR(epc)) {
|
|
dev_err(dev, "failed to create epc device\n");
|
|
err = PTR_ERR(epc);
|
|
goto err_pm_put;
|
|
}
|
|
|
|
epc->max_functions = ep->max_functions;
|
|
epc_set_drvdata(epc, ep);
|
|
|
|
rcar_pcie_ep_hw_init(pcie);
|
|
|
|
err = pci_epc_multi_mem_init(epc, ep->ob_window, ep->num_ob_windows);
|
|
if (err < 0) {
|
|
dev_err(dev, "failed to initialize the epc memory space\n");
|
|
goto err_pm_put;
|
|
}
|
|
|
|
return 0;
|
|
|
|
err_pm_put:
|
|
pm_runtime_put(dev);
|
|
|
|
err_pm_disable:
|
|
pm_runtime_disable(dev);
|
|
|
|
return err;
|
|
}
|
|
|
|
static struct platform_driver rcar_pcie_ep_driver = {
|
|
.driver = {
|
|
.name = "rcar-pcie-ep",
|
|
.of_match_table = rcar_pcie_ep_of_match,
|
|
.suppress_bind_attrs = true,
|
|
},
|
|
.probe = rcar_pcie_ep_probe,
|
|
};
|
|
builtin_platform_driver(rcar_pcie_ep_driver);
|