// SPDX-License-Identifier: GPL-2.0
/*
* PCIe driver for Renesas R-Car SoCs
* Copyright (C) 2014-2020 Renesas Electronics Europe Ltd
*
* Based on:
* arch/sh/drivers/pci/pcie-sh7786.c
* arch/sh/drivers/pci/ops-sh7786.c
* Copyright (C) 2009 - 2011 Paul Mundt
*
* Author: Phil Edworthy <phil.edworthy@renesas.com>
*/
#include <linux/bitops.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/irqdomain.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/iopoll.h>
#include <linux/msi.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/of_platform.h>
#include <linux/pci.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
#include "pcie-rcar.h"
struct rcar_msi {
DECLARE_BITMAP(used, INT_PCI_MSI_NR);
struct irq_domain *domain;
struct mutex map_lock;
spinlock_t mask_lock;
int irq1;
int irq2;
};
/* Structure representing the PCIe interface */
struct rcar_pcie_host {
struct rcar_pcie pcie;
struct phy *phy;
struct clk *bus_clk;
struct rcar_msi msi;
int (*phy_init_fn)(struct rcar_pcie_host *host);
};
static DEFINE_SPINLOCK(pmsr_lock);
static int rcar_pcie_wakeup(struct device *pcie_dev, void __iomem *pcie_base)
{
unsigned long flags;
u32 pmsr, val;
int ret = 0;
spin_lock_irqsave(&pmsr_lock, flags);
if (!pcie_base || pm_runtime_suspended(pcie_dev)) {
ret = -EINVAL;
goto unlock_exit;
}
pmsr = readl(pcie_base + PMSR);
/*
* Test if the PCIe controller received PM_ENTER_L1 DLLP and
* the PCIe controller is not in L1 link state. If true, apply
* fix, which will put the controller into L1 link state, from
* which it can return to L0s/L0 on its own.
*/
if ((pmsr & PMEL1RX) && ((pmsr & PMSTATE) != PMSTATE_L1)) {
writel(L1IATN, pcie_base + PMCTLR);
ret = readl_poll_timeout_atomic(pcie_base + PMSR, val,
val & L1FAEG, 10, 1000);
if (ret) {
dev_warn_ratelimited(pcie_dev,
"Timeout waiting for L1 link state, ret=%d\n",
ret);
}
writel(L1FAEG | PMEL1RX, pcie_base + PMSR);
}
unlock_exit:
spin_unlock_irqrestore(&pmsr_lock, flags);
return ret;
}
static struct rcar_pcie_host *msi_to_host(struct rcar_msi *msi)
{
return container_of(msi, struct rcar_pcie_host, msi);
}
static u32 rcar_read_conf(struct rcar_pcie *pcie, int where)
{
unsigned int shift = BITS_PER_BYTE * (where & 3);
u32 val = rcar_pci_read_reg(pcie, where & ~3);
return val >> shift;
}
#ifdef CONFIG_ARM
#define __rcar_pci_rw_reg_workaround(instr) \
" .arch armv7-a\n" \
"1: " instr " %1, [%2]\n" \
"2: isb\n" \
"3: .pushsection .text.fixup,\"ax\"\n" \
" .align 2\n" \
"4: mov %0, #" __stringify(PCIBIOS_SET_FAILED) "\n" \
" b 3b\n" \
" .popsection\n" \
" .pushsection __ex_table,\"a\"\n" \
" .align 3\n" \
" .long 1b, 4b\n" \
" .long 2b, 4b\n" \
" .popsection\n"
#endif
static int rcar_pci_write_reg_workaround(struct rcar_pcie *pcie, u32 val,
unsigned int reg)
{
int error = PCIBIOS_SUCCESSFUL;
#ifdef CONFIG_ARM
asm volatile(
__rcar_pci_rw_reg_workaround("str")
: "+r"(error):"r"(val), "r"(pcie->base + reg) : "memory");
#else
rcar_pci_write_reg(pcie, val, reg);
#endif
return error;
}
static int rcar_pci_read_reg_workaround(struct rcar_pcie *pcie, u32 *val,
unsigned int reg)
{
int error = PCIBIOS_SUCCESSFUL;
#ifdef CONFIG_ARM
asm volatile(
__rcar_pci_rw_reg_workaround("ldr")
: "+r"(error), "=r"(*val) : "r"(pcie->base + reg) : "memory");
if (error != PCIBIOS_SUCCESSFUL)
PCI_SET_ERROR_RESPONSE(val);
#else
*val = rcar_pci_read_reg(pcie, reg);
#endif
return error;
}
/* Serialization is provided by 'pci_lock' in drivers/pci/access.c */
static int rcar_pcie_config_access(struct rcar_pcie_host *host,
unsigned char access_type, struct pci_bus *bus