388 lines
11 KiB
C
388 lines
11 KiB
C
/* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */
|
|
/*
|
|
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
|
|
*/
|
|
|
|
#ifndef _PCIE_AMLOGIC_V3_H
|
|
#define _PCIE_AMLOGIC_V3_H
|
|
#include <linux/pci.h>
|
|
#include <linux/kernel.h>
|
|
/*
|
|
* The upper 16 bits of PCIE_CLIENT_CONFIG are a write mask for the lower 16
|
|
* bits. This allows atomic updates of the register without locking.
|
|
*/
|
|
#define PCIE_BASIC_STATUS 0x018
|
|
#define LINK_UP_MASK 0xff
|
|
|
|
#define PCIE_CFGCTRL 0x084
|
|
#define PCIE_PCI_IDS0 0x098
|
|
#define PCIE_PCI_IDS1 0x09c
|
|
#define PCIE_PCI_IDS2 0x0a0
|
|
#define PCIE_PCI_IRQ 0x0a8
|
|
#define PCIE_BAR_0 0x0e4
|
|
#define PCIE_BAR_1 0x0e8
|
|
#define PCIE_BAR_2 0x0ec
|
|
#define PCIE_BAR_3 0x0f0
|
|
#define PCIE_BAR_4 0x0f4
|
|
#define PCIE_BAR_5 0x0f8
|
|
#define PCIE_BAR_IO BIT(1)
|
|
#define PCIE_BAR_MEM_TYPE_64 BIT(2)
|
|
#define PCIE_BAR_MEM_TYPE_PREFETCH BIT(3)
|
|
#define PCIE_BAR_MEM_MASK (~0x0fUL)
|
|
#define PCIE_BAR_IO_MASK (~0x03UL)
|
|
|
|
#define PHYMAC_CFG 0x33c
|
|
#define PCIE_CFGNUM 0x140
|
|
#define IMASK_LOCAL 0x180
|
|
#define ISTATUS_LOCAL 0x184
|
|
#define IMASK_HOST 0x188
|
|
#define ISTATUS_HOST 0x18c
|
|
#define IMSI_ADDR 0x190
|
|
#define ISTATUS_MSI 0x194
|
|
#define ATR_PCIE_WIN0 0x600
|
|
#define ATR_PCIE_WIN1 0x700
|
|
#define ATR_AXI4_SLV0 0x800
|
|
|
|
#define ATR_TABLE_SIZE 0x20
|
|
#define ATR_SRC_ADDR_LOW 0x0
|
|
#define ATR_SRC_ADDR_HIGH 0x4
|
|
#define ATR_TRSL_ADDR_LOW 0x8
|
|
#define ATR_TRSL_ADDR_HIGH 0xc
|
|
#define ATR_TRSL_PARAM 0x10
|
|
|
|
#define ATR_TRSLID_AXIDEVICE (0x420004)
|
|
/* Write-through, read/write allocate */
|
|
#define ATR_TRSLID_AXIMEMORY (0x4e0004)
|
|
#define ATR_TRSLID_PCIE_CONF (0x000001)
|
|
#define ATR_TRSLID_PCIE_IO (0x020000)
|
|
/*#define ATR_TRSLID_PCIE_IO (0x020001)*/
|
|
#define ATR_TRSLID_PCIE_MEMORY (0x000000)
|
|
|
|
#define INT_AXI_POST_ERROR BIT(16)
|
|
#define INT_AXI_FETCH_ERROR BIT(17)
|
|
#define INT_AXI_DISCARD_ERROR BIT(18)
|
|
#define INT_PCIE_POST_ERROR BIT(20)
|
|
#define INT_PCIE_FETCH_ERROR BIT(21)
|
|
#define INT_PCIE_DISCARD_ERROR BIT(22)
|
|
#define INT_ERRORS (INT_AXI_POST_ERROR | INT_AXI_FETCH_ERROR | \
|
|
INT_AXI_DISCARD_ERROR | INT_PCIE_POST_ERROR | \
|
|
INT_PCIE_FETCH_ERROR | INT_PCIE_DISCARD_ERROR)
|
|
|
|
#define INTA_OFFSET 24
|
|
#define INTA BIT(24)
|
|
#define INTB BIT(25)
|
|
#define INTC BIT(26)
|
|
#define INTD BIT(27)
|
|
#define INT_MSI BIT(28)
|
|
#define INT_INTX_MASK (INTA | INTB | INTC | INTD)
|
|
#define INT_MASK (INT_INTX_MASK | INT_MSI | INT_ERRORS)
|
|
|
|
#define INTX_NUM 4
|
|
#define INT_PCI_MSI_NR 32
|
|
|
|
#define DWORD_MASK 3
|
|
|
|
#define PCI_CFG_SPACE 0x1000
|
|
#define PCIE_HEADER_TYPE_OFFSET (PCI_CFG_SPACE + 0x00)
|
|
#define PCIE_CAP_OFFSET (PCI_CFG_SPACE + 0x80)
|
|
|
|
#define PCIE_ECAM_BUS(x) (((x) & 0xff) << 20)
|
|
#define PCIE_ECAM_DEV(x) (((x) & 0x1f) << 15)
|
|
#define PCIE_ECAM_FUNC(x) (((x) & 0x7) << 12)
|
|
#define PCIE_ECAM_REG(x) (((x) & 0xfff) << 0)
|
|
#define PCIE_ECAM_ADDR(bus, dev, func, reg) \
|
|
(PCIE_ECAM_BUS(bus) | PCIE_ECAM_DEV(dev) | \
|
|
PCIE_ECAM_FUNC(func) | PCIE_ECAM_REG(reg))
|
|
|
|
#define PCIE_A_CTRL0 0x0
|
|
#define PORT_TYPE BIT(0)
|
|
#define PCIE_A_CTRL1 0x4
|
|
#define PCIE_A_CTRL2 0x8
|
|
#define PCIE_A_CTRL3 0xc
|
|
#define PCIE_A_CTRL4 0x10
|
|
#define PCIE_A_CTRL5 0x14
|
|
#define PCIE_A_CTRL6 0x18
|
|
|
|
#define EP_BASE_OFFSET 0
|
|
#define AMLOGIC_PCIE_EP_FUNC_BASE(fn) (((fn) << 12) & GENMASK(19, 12))
|
|
#define EP_FUNC_MSI_CAP_OFFSET 0x0e0
|
|
|
|
#define RESETCTRL0_OFFSET 0x0
|
|
#define RESETCTRL1_OFFSET 0x4
|
|
#define RESETCTRL3_OFFSET 0xc
|
|
|
|
#define WAIT_LINKUP_TIMEOUT 180000
|
|
|
|
#define LTSSM_L0 0x10
|
|
#define LTSSM_L1_IDLE 0x13
|
|
#define LTSSM_L2_IDLE 0x14
|
|
#define LTSSM_LPBK_ACTIVE 0x18
|
|
|
|
#define PCIE_LINK_STATE_CHECK(val, state) \
|
|
(((((val) >> 18)) & GENMASK(4, 0)) == (state))
|
|
|
|
/* amlogic phy V1 register offset */
|
|
#define UPCTX_CONT0_REG2 (0x0009 << 2)
|
|
#define UPCTX_CONT1_REG2 (0x000e << 2)
|
|
#define UPCTX_DR_REG0 (0x0011 << 2)
|
|
#define UPCTX_CTRL_REG (0x0012 << 2)
|
|
#define UPCRX_DR_REG0 (0x0014 << 2)
|
|
#define UPCRX_DR_REG1 (0x0015 << 2)
|
|
#define REG_MISC_GEN1_REG (0x001a << 2)
|
|
#define REG_DCHA_AFE_LPBK_REG (0x001e << 2)
|
|
#define REG_DCHA_AFE_GEN1_REG (0x001f << 2)
|
|
#define REG_DCHA_DFE_GEN1_REG (0x0024 << 2)
|
|
#define REG_DCHD_CDR_GEN1_REG (0x002e << 2)
|
|
#define REG_DCHD_EQ_LPBK_REG (0x0032 << 2)
|
|
#define REG_DCHD_EQ_GEN1_REG (0x0033 << 2)
|
|
#define REG_DCHD_EQ_GEN2_REG (0x0034 << 2)
|
|
#define REG_DCHD_EQ_GEN3_REG (0x0035 << 2)
|
|
#define SAMPLE_1ST_2ND_TIME (0x0042 << 2)
|
|
#define SW_CTRL_PMA_REG0 (0x0044 << 2)
|
|
#define SAMPLE_3RD_TIME (0x0043 << 2)
|
|
#define EQVAL_CTRL_REG (0x0061 << 2)
|
|
#define DELAY_TIME24 (0x0065 << 2)
|
|
|
|
enum pcie_data_rate {
|
|
PCIE_GEN1,
|
|
PCIE_GEN2,
|
|
PCIE_GEN3,
|
|
PCIE_GEN4
|
|
};
|
|
|
|
enum pcie_phy_type {
|
|
M31_PHY,
|
|
M31_COMBPHY,
|
|
AMLOGIC_PHY,
|
|
};
|
|
|
|
enum aml_phy_pll_version {
|
|
AMLOGIC_PHY_PLL_V1 = 1,
|
|
AMLOGIC_PHY_PLL_V2,
|
|
AMLOGIC_PHY_PLL_V3,
|
|
};
|
|
|
|
struct amlogic_pcie {
|
|
void __iomem *apb_base; /* pcie_axi_lite for internal*/
|
|
void __iomem *axi_base; /* pcie_ctrl for data*/
|
|
void __iomem *pcictrl_base; /* pcie_A_ctrl for oneself*/
|
|
void __iomem *phy_base; /* pcie_gen3_phy*/
|
|
void __iomem *rst_base; /* reset_ctrl*/
|
|
void __iomem *ecam_base; /* ecam*/
|
|
phys_addr_t ecam_bus_base;
|
|
u32 ecam_size;
|
|
|
|
phys_addr_t io_bus_addr;
|
|
u32 io_size;
|
|
u32 mem_size;
|
|
phys_addr_t mem_bus_addr;
|
|
|
|
struct phy *pcie_phy;
|
|
u32 phy_type;
|
|
|
|
int reset_gpio;
|
|
bool do_reset_gpio;
|
|
u32 gpio_type;
|
|
|
|
struct clk *pcie_400m_clk;
|
|
struct clk *pcie_tl_clk;
|
|
struct clk *cts_pcie_clk;
|
|
struct clk *pcie_clk;
|
|
struct clk *phy_clk;
|
|
struct clk *refpll_clk;
|
|
struct clk *dev_clk;
|
|
|
|
struct reset_control *m31phy_rst;/*RESETCTRL_RESET1 bit 21*/
|
|
struct reset_control *amlphy_rst;
|
|
struct reset_control *gen3_l0_rst; /*RESETCTRL_RESET1 bit 18*/
|
|
struct reset_control *gen2_l0_rst;
|
|
struct reset_control *u3p2_phy_apb_rst;
|
|
struct reset_control *pcie_apb_rst; /*RESETCTRL_RESET1 bit 14*/
|
|
struct reset_control *pcie_phy_rst; /*RESETCTRL_RESET1 bit 13*/
|
|
struct reset_control *pcie_pipe_rst;
|
|
struct reset_control *pcie_a_rst; /*RESETCTRL_RESET1 bit 12*/
|
|
struct reset_control *pcie_rst0; /*RESETCTRL_RESET3 bit 12*/
|
|
struct reset_control *pcie_rst1; /*RESETCTRL_RESET3 bit 13*/
|
|
struct reset_control *pcie_rst2; /*RESETCTRL_RESET3 bit 14*/
|
|
struct reset_control *pcie_rst3; /*RESETCTRL_RESET3 bit 15*/
|
|
struct reset_control *pcie_rst4; /*RESETCTRL_RESET3 bit 16*/
|
|
struct reset_control *pcie_rst5; /*RESETCTRL_RESET3 bit 17*/
|
|
struct reset_control *pcie_rst6; /*RESETCTRL_RESET3 bit 18*/
|
|
struct reset_control *pcie_rst7; /*RESETCTRL_RESET3 bit 19*/
|
|
|
|
struct device *dev;
|
|
|
|
struct pinctrl *p;
|
|
|
|
u32 m31phy_rst_bit;
|
|
u32 amlphy_rst_bit;
|
|
u32 gen3_l0_rst_bit;
|
|
u32 gen2_l0_rst_bit;
|
|
u32 u3p2_phy_apb_rst_bit;
|
|
u32 apb_rst_bit;
|
|
u32 phy_rst_bit;
|
|
u32 pcie_pipe_bit;
|
|
u32 pcie_a_rst_bit;
|
|
u32 pcie_rst_bit;
|
|
u32 pcie_rst_mask;
|
|
u32 pll_setting_number;
|
|
|
|
bool is_rc;
|
|
|
|
u32 lanes;
|
|
u32 port_num;
|
|
|
|
u8 lanes_map;
|
|
u32 link_gen;
|
|
u32 link_times;
|
|
int offset;
|
|
struct resource *mem_res;
|
|
};
|
|
|
|
#define PTR_ALIGN_DOWN(p, a) ((typeof(p))ALIGN_DOWN((unsigned long)(p), (a)))
|
|
|
|
static inline u32 amlogic_pcie_read_sz(void __iomem *addr, int size)
|
|
{
|
|
void __iomem *aligned_addr = PTR_ALIGN_DOWN(addr, 0x4);
|
|
unsigned int offset = (unsigned long)addr & 0x3;
|
|
u32 val = readl(aligned_addr);
|
|
|
|
if (!IS_ALIGNED((uintptr_t)addr, size)) {
|
|
pr_warn("Address %p and size %d are not aligned\n", addr, size);
|
|
return 0;
|
|
}
|
|
|
|
if (size > 2)
|
|
return val;
|
|
|
|
return (val >> (8 * offset)) & ((1 << (size * 8)) - 1);
|
|
}
|
|
|
|
static inline void amlogic_pcie_write_sz(void __iomem *addr, int size,
|
|
u32 value)
|
|
{
|
|
void __iomem *aligned_addr = PTR_ALIGN_DOWN(addr, 0x4);
|
|
unsigned int offset = (unsigned long)addr & 0x3;
|
|
u32 mask;
|
|
u32 val;
|
|
|
|
if (!IS_ALIGNED((uintptr_t)addr, size)) {
|
|
pr_warn("Address %p and size %d are not aligned\n", addr, size);
|
|
return;
|
|
}
|
|
|
|
if (size > 2) {
|
|
writel(value, addr);
|
|
return;
|
|
}
|
|
|
|
mask = ~(((1 << (size * 8)) - 1) << (offset * 8));
|
|
val = readl(aligned_addr) & mask;
|
|
val |= value << (offset * 8);
|
|
writel(val, aligned_addr);
|
|
}
|
|
|
|
static inline void amlogic_pcie_ep_fn_writeb(struct amlogic_pcie *pcie, u8 fn,
|
|
u32 reg, u8 value)
|
|
{
|
|
void __iomem *addr = pcie->ecam_base + EP_BASE_OFFSET +
|
|
AMLOGIC_PCIE_EP_FUNC_BASE(fn) + reg;
|
|
|
|
amlogic_pcie_write_sz(addr, 0x1, value);
|
|
}
|
|
|
|
static inline void amlogic_pcie_ep_fn_writew(struct amlogic_pcie *pcie, u8 fn,
|
|
u32 reg, u16 value)
|
|
{
|
|
void __iomem *addr = pcie->ecam_base + EP_BASE_OFFSET +
|
|
AMLOGIC_PCIE_EP_FUNC_BASE(fn) + reg;
|
|
|
|
amlogic_pcie_write_sz(addr, 0x2, value);
|
|
}
|
|
|
|
static inline void amlogic_pcie_ep_fn_writel(struct amlogic_pcie *pcie, u8 fn,
|
|
u32 reg, u32 value)
|
|
{
|
|
writel(value, pcie->ecam_base + EP_BASE_OFFSET +
|
|
AMLOGIC_PCIE_EP_FUNC_BASE(fn) + reg);
|
|
}
|
|
|
|
static inline u16 amlogic_pcie_ep_fn_readw(struct amlogic_pcie *pcie,
|
|
u8 fn, u32 reg)
|
|
{
|
|
void __iomem *addr = pcie->ecam_base + EP_BASE_OFFSET +
|
|
AMLOGIC_PCIE_EP_FUNC_BASE(fn) + reg;
|
|
|
|
return amlogic_pcie_read_sz(addr, 0x2);
|
|
}
|
|
|
|
static inline u32 amlogic_pcie_ep_fn_readl(struct amlogic_pcie *pcie,
|
|
u8 fn, u32 reg)
|
|
{
|
|
return readl(pcie->ecam_base + EP_BASE_OFFSET +
|
|
AMLOGIC_PCIE_EP_FUNC_BASE(fn) + reg);
|
|
}
|
|
|
|
static inline u16 amlogic_pcieinter_readw(struct amlogic_pcie *pcie, u32 reg)
|
|
{
|
|
return readw(pcie->ecam_base +
|
|
PCIE_ECAM_ADDR(0, 0, 0, reg));
|
|
}
|
|
|
|
static inline void amlogic_pcieinter_writew(struct amlogic_pcie *pcie, u16 val,
|
|
u32 reg)
|
|
{
|
|
writew(val, pcie->ecam_base +
|
|
PCIE_ECAM_ADDR(0, 0, 0, reg));
|
|
}
|
|
|
|
static inline u32 amlogic_pcieinter_read(struct amlogic_pcie *pcie, u32 reg)
|
|
{
|
|
if (reg < PCI_CFG_SPACE)
|
|
return readl(pcie->apb_base + reg);
|
|
else
|
|
return readl(pcie->ecam_base +
|
|
PCIE_ECAM_ADDR(0, 0, 0, reg - PCI_CFG_SPACE));
|
|
}
|
|
|
|
static inline void amlogic_pcieinter_write(struct amlogic_pcie *pcie, u32 val,
|
|
u32 reg)
|
|
{
|
|
if (reg < PCI_CFG_SPACE)
|
|
writel(val, pcie->apb_base + reg);
|
|
else
|
|
writel(val, pcie->ecam_base +
|
|
PCIE_ECAM_ADDR(0, 0, 0, reg - PCI_CFG_SPACE));
|
|
}
|
|
|
|
static inline u32 amlogic_pciectrl_read(struct amlogic_pcie *pcie, u32 reg)
|
|
{
|
|
return readl(pcie->pcictrl_base + reg);
|
|
}
|
|
|
|
static inline void amlogic_pciectrl_write(struct amlogic_pcie *pcie, u32 val,
|
|
u32 reg)
|
|
{
|
|
writel(val, pcie->pcictrl_base + reg);
|
|
}
|
|
|
|
int amlogic_pcie_set_reset(struct amlogic_pcie *amlogic, bool set);
|
|
int amlogic_pcie_parse_dt(struct amlogic_pcie *amlogic);
|
|
int amlogic_pcie_init_port(struct amlogic_pcie *amlogic);
|
|
int amlogic_pcie_get_phys(struct amlogic_pcie *amlogic);
|
|
void amlogic_pcie_deinit_phys(struct amlogic_pcie *amlogic);
|
|
int amlogic_pcie_enable_clocks(struct amlogic_pcie *amlogic);
|
|
void amlogic_pcie_disable_clocks(struct amlogic_pcie *amlogic);
|
|
bool amlogic_pcie_link_up(struct amlogic_pcie *amlogic);
|
|
void amlogic_set_max_rd_req_size(struct amlogic_pcie *amlogic, int size);
|
|
void amlogic_set_max_payload(struct amlogic_pcie *amlogic, int size);
|
|
void amlogic_pcie_cfg_addr_map(struct amlogic_pcie *amlogic,
|
|
unsigned int atr_base,
|
|
u64 src_addr,
|
|
u64 trsl_addr,
|
|
int size,
|
|
int trsl_param);
|
|
int amlogic_pcie_set_reset_for_aml_phy(struct amlogic_pcie *amlogic);
|
|
int amlogic_pcie_set_phy_pll(struct amlogic_pcie *amlogic);
|
|
#endif /* _PCIE_AMLOGIC_V3_H */
|