// SPDX-License-Identifier: GPL-2.0
/*
* Copyright Altera Corporation (C) 2013-2015. All rights reserved
*
* Author: Ley Foon Tan <lftan@altera.com>
* Description: Altera PCIe host controller driver
*/
#include <linux/bitfield.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/irqchip/chained_irq.h>
#include <linux/irqdomain.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_pci.h>
#include <linux/pci.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include "../pci.h"
#define RP_TX_REG0 0x2000
#define RP_TX_REG1 0x2004
#define RP_TX_CNTRL 0x2008
#define RP_TX_EOP 0x2
#define RP_TX_SOP 0x1
#define RP_RXCPL_STATUS 0x2010
#define RP_RXCPL_EOP 0x2
#define RP_RXCPL_SOP 0x1
#define RP_RXCPL_REG0 0x2014
#define RP_RXCPL_REG1 0x2018
#define P2A_INT_STATUS 0x3060
#define P2A_INT_STS_ALL 0xf
#define P2A_INT_ENABLE 0x3070
#define P2A_INT_ENA_ALL 0xf
#define RP_LTSSM 0x3c64
#define RP_LTSSM_MASK 0x1f
#define LTSSM_L0 0xf
#define S10_RP_TX_CNTRL 0x2004
#define S10_RP_RXCPL_REG 0x2008
#define S10_RP_RXCPL_STATUS 0x200C
#define S10_RP_CFG_ADDR(pcie, reg) \
(((pcie)->hip_base) + (reg) + (1 << 20))
#define S10_RP_SECONDARY(pcie) \
readb(S10_RP_CFG_ADDR(pcie, PCI_SECONDARY_BUS))
/* TLP configuration type 0 and 1 */
#define TLP_FMTTYPE_CFGRD0 0x04 /* Configuration Read Type 0 */
#define TLP_FMTTYPE_CFGWR0 0x44 /* Configuration Write Type 0 */
#define TLP_FMTTYPE_CFGRD1 0x05 /* Configuration Read Type 1 */
#define TLP_FMTTYPE_CFGWR1 0x45 /* Configuration Write Type 1 */
#define TLP_PAYLOAD_SIZE 0x01
#define TLP_READ_TAG 0x1d
#define TLP_WRITE_TAG 0x10
#define RP_DEVFN 0
#define TLP_CFG_DW0(pcie, cfg) \
(((cfg) << 24) | \
TLP_PAYLOAD_SIZE)
#define TLP_CFG_DW1(pcie, tag, be) \
(((PCI_DEVID(pcie->root_bus_nr, RP_DEVFN)) << 16) | (tag << 8) | (be))
#define TLP_CFG_DW2(bus, devfn, offset) \
(((bus) << 24) | ((devfn) << 16) | (offset))
#define TLP_COMP_STATUS(s) (((s) >> 13) & 7)
#define TLP_BYTE_COUNT(s) (((s) >> 0) & 0xfff)
#define TLP_HDR_SIZE 3
#define TLP_LOOP 500
#define LINK_UP_TIMEOUT HZ
#define LINK_RETRAIN_TIMEOUT HZ
#define DWORD_MASK 3
#define S10_TLP_FMTTYPE_CFGRD0 0x05
#define S10_TLP_FMTTYPE_CFGRD1 0x04
#define S10_TLP_FMTTYPE_CFGWR0 0x45
#define S10_TLP_FMTTYPE_CFGWR1 0x44
#define AGLX_RP_CFG_ADDR(pcie, reg) (((pcie)->hip_base) + (reg))
#define AGLX_RP_SECONDARY(pcie) \
readb(AGLX_RP_CFG_ADDR(pcie, PCI_SECONDARY_BUS))
#define AGLX_BDF_REG 0x00002004
#define AGLX_ROOT_PORT_IRQ_STATUS 0x14c
#define AGLX_ROOT_PORT_IRQ_ENABLE 0x150
#define CFG_AER BIT(4)
#define AGLX_CFG_TARGET GENMASK(13, 12)
#define AGLX_CFG_TARGET_TYPE0 0
#define AGLX_CFG_TARGET_TYPE1 1
#define AGLX_CFG_TARGET_LOCAL_2000 2
#define AGLX_CFG_TARGET_LOCAL_3000 3
enum altera_pcie_version {
ALTERA_PCIE_V1 = 0,
ALTERA_PCIE_V2,
ALTERA_PCIE_V3,
};
struct altera_pcie {
struct platform_device *pdev;
void __iomem *cra_base;
void __iomem *hip_base;
int irq;
u8 root_bus_nr;
struct irq_domain *irq_domain;
struct resource bus_range;
const struct altera_pcie_data *pcie_data;
};
struct altera_pcie_ops {
int (*tlp_read_pkt)(struct altera_pcie *pcie, u32 *value);
void (*tlp_write_pkt)(struct altera_pcie *pcie, u32 *headers,
u32 data, bool align);
bool (*get_link_status)(struct altera_pcie *pcie);
int (*rp_read_cfg)(struct altera_pcie *pcie, int where,
int size, u32 *value);
int (*rp_write_cfg)(struct altera_pcie *pcie, u8 busno,
int where, int size, u32 value);
int (*ep_read_cfg)(struct altera_pcie *pcie, u8 busno,
unsigned int devfn, int where, int size, u32 *value);
int (*ep_write_cfg)(struct altera_pcie *pcie, u8 busno,
unsigned int devfn, int where, int size, u32 value);
void (*rp_isr)(struct irq_desc *desc);
};
struct altera_pcie_data {
const struct altera_pcie_ops *ops;
enum altera_pcie_version version;
u32 cap_offset; /* PCIe capability structure register offset */
u32 cfgrd0;
u32 cfgrd1;
u32 cfgwr0;
u32 cfgwr1;
u32 port_conf_offset;
u32 port_irq_status_offset;
u32 port_irq_enable_offset;
};
struct tlp_rp_regpair_t {
u32 ctrl;
u32 reg0;
u32 reg1;
};
static inline void cra_writel(struct altera_pcie *pcie, const u32 value,
const u32 reg)
{
writel_relaxed(value, pcie->cra_base + reg);
}
static inline u32 cra_readl(struct altera_pcie *pcie, const u32 reg)
{
return readl_relaxed(pcie->cra_base + reg);
}
static inline void cra_writew(struct altera_pcie *pcie, const u32 value,
const u32 reg)
{
writew_relaxed(value, pcie->cra_base + reg);
}
static inline u32