1
linux/drivers/pci/controller/pcie-rcar-ep.c
Manivannan Sadhasivam a01e7214be
PCI: endpoint: Remove "core_init_notifier" flag
"core_init_notifier" flag is set by the glue drivers requiring refclk from
the host to complete the DWC core initialization. Also, those drivers will
send a notification to the EPF drivers once the initialization is fully
completed using the pci_epc_init_notify() API. Only then, the EPF drivers
will start functioning.

For the rest of the drivers generating refclk locally, EPF drivers will
start functioning post binding with them. EPF drivers rely on the
'core_init_notifier' flag to differentiate between the drivers.
Unfortunately, this creates two different flows for the EPF drivers.

So to avoid that, let's get rid of the "core_init_notifier" flag and follow
a single initialization flow for the EPF drivers. This is done by calling
the dw_pcie_ep_init_notify() from all glue drivers after the completion of
dw_pcie_ep_init_registers() API. This will allow all the glue drivers to
send the notification to the EPF drivers once the initialization is fully
completed.

Only difference here is that, the drivers requiring refclk from host will
send the notification once refclk is received, while others will send it
during probe time itself.

But this also requires the EPC core driver to deliver the notification
after EPF driver bind. Because, the glue driver can send the notification
before the EPF drivers bind() and in those cases the EPF drivers will miss
the event. To accommodate this, EPC core is now caching the state of the
EPC initialization in 'init_complete' flag and pci-ep-cfs driver sends the
notification to EPF drivers based on that after each EPF driver bind.

Link: https://lore.kernel.org/linux-pci/20240327-pci-dbi-rework-v12-8-082625472414@linaro.org
Tested-by: Niklas Cassel <cassel@kernel.org>
Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
Signed-off-by: Krzysztof Wilczyński <kwilczynski@kernel.org>
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Reviewed-by: Niklas Cassel <cassel@kernel.org>
2024-04-10 17:52:42 +00:00

567 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/delay.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <linux/pci.h>
#include <linux/pci-epc.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.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), PCI_HEADER_TYPE_MASK,
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,
unsigned int type, u16 interrupt_num)
{
struct rcar_pcie_endpoint *ep = epc_get_drvdata(epc);
switch (type) {
case PCI_IRQ_INTX:
return rcar_pcie_ep_assert_intx(ep, fn, 0);
case PCI_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 */
.bar[BAR_0] = { .type = BAR_FIXED, .fixed_size = 128,
.only_64bit = true, },
.bar[BAR_1] = { .type = BAR_RESERVED, },
.bar[BAR_2] = { .type = BAR_FIXED, .fixed_size = 256,
.only_64bit = true, },
.bar[BAR_3] = { .type = BAR_RESERVED, },
.bar[BAR_4] = { .type = BAR_FIXED, .fixed_size = 256,
.only_64bit = true, },
.bar[BAR_5] = { .type = BAR_RESERVED, },
};
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;
}
pci_epc_init_notify(epc);
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);