// SPDX-License-Identifier: GPL-2.0
/*
* PCIe driver for Marvell Armada 370 and Armada XP SoCs
*
* Author: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
*/
#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/init.h>
#include <linux/mbus.h>
#include <linux/msi.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/of_gpio.h>
#include <linux/of_pci.h>
#include <linux/of_platform.h>
#include "../pci.h"
/*
* PCIe unit register offsets.
*/
#define PCIE_DEV_ID_OFF 0x0000
#define PCIE_CMD_OFF 0x0004
#define PCIE_DEV_REV_OFF 0x0008
#define PCIE_BAR_LO_OFF(n) (0x0010 + ((n) << 3))
#define PCIE_BAR_HI_OFF(n) (0x0014 + ((n) << 3))
#define PCIE_CAP_PCIEXP 0x0060
#define PCIE_HEADER_LOG_4_OFF 0x0128
#define PCIE_BAR_CTRL_OFF(n) (0x1804 + (((n) - 1) * 4))
#define PCIE_WIN04_CTRL_OFF(n) (0x1820 + ((n) << 4))
#define PCIE_WIN04_BASE_OFF(n) (0x1824 + ((n) << 4))
#define PCIE_WIN04_REMAP_OFF(n) (0x182c + ((n) << 4))
#define PCIE_WIN5_CTRL_OFF 0x1880
#define PCIE_WIN5_BASE_OFF 0x1884
#define PCIE_WIN5_REMAP_OFF 0x188c
#define PCIE_CONF_ADDR_OFF 0x18f8
#define PCIE_CONF_ADDR_EN 0x80000000
#define PCIE_CONF_REG(r) ((((r) & 0xf00) << 16) | ((r) & 0xfc))
#define PCIE_CONF_BUS(b) (((b) & 0xff) << 16)
#define PCIE_CONF_DEV(d) (((d) & 0x1f) << 11)
#define PCIE_CONF_FUNC(f) (((f) & 0x7) << 8)
#define PCIE_CONF_ADDR(bus, devfn, where) \
(PCIE_CONF_BUS(bus) | PCIE_CONF_DEV(PCI_SLOT(devfn)) | \
PCIE_CONF_FUNC(PCI_FUNC(devfn)) | PCIE_CONF_REG(where) | \
PCIE_CONF_ADDR_EN)
#define PCIE_CONF_DATA_OFF 0x18fc
#define PCIE_MASK_OFF 0x1910
#define PCIE_MASK_ENABLE_INTS 0x0f000000
#define PCIE_CTRL_OFF 0x1a00
#define PCIE_CTRL_X1_MODE 0x0001
#define PCIE_STAT_OFF 0x1a04
#define PCIE_STAT_BUS 0xff00
#define PCIE_STAT_DEV 0x1f0000
#define PCIE_STAT_LINK_DOWN BIT(0)
#define PCIE_RC_RTSTA 0x1a14
#define PCIE_DEBUG_CTRL 0x1a60
#define PCIE_DEBUG_SOFT_RESET BIT(20)
enum {
PCISWCAP = PCI_BRIDGE_CONTROL + 2,
PCISWCAP_EXP_LIST_ID = PCISWCAP + PCI_CAP_LIST_ID,
PCISWCAP_EXP_DEVCAP = PCISWCAP + PCI_EXP_DEVCAP,
PCISWCAP_EXP_DEVCTL = PCISWCAP + PCI_EXP_DEVCTL,
PCISWCAP_EXP_LNKCAP = PCISWCAP + PCI_EXP_LNKCAP,
PCISWCAP_EXP_LNKCTL = PCISWCAP + PCI_EXP_LNKCTL,
PCISWCAP_EXP_SLTCAP = PCISWCAP + PCI_EXP_SLTCAP,
PCISWCAP_EXP_SLTCTL = PCISWCAP + PCI_EXP_SLTCTL,
PCISWCAP_EXP_RTCTL = PCISWCAP + PCI_EXP_RTCTL,
PCISWCAP_EXP_RTSTA = PCISWCAP + PCI_EXP_RTSTA,
PCISWCAP_EXP_DEVCAP2 = PCISWCAP + PCI_EXP_DEVCAP2,
PCISWCAP_EXP_DEVCTL2 = PCISWCAP + PCI_EXP_DEVCTL2,
PCISWCAP_EXP_LNKCAP2 = PCISWCAP + PCI_EXP_LNKCAP2,
PCISWCAP_EXP_LNKCTL2 = PCISWCAP + PCI_EXP_LNKCTL2,
PCISWCAP_EXP_SLTCAP2 = PCISWCAP + PCI_EXP_SLTCAP2,
PCISWCAP_EXP_SLTCTL2 = PCISWCAP + PCI_EXP_SLTCTL2,
};
/* PCI configuration space of a PCI-to-PCI bridge */
struct mvebu_sw_pci_bridge {
u16 vendor;
u16 device;
u16 command;
u16 status;
u16 class;
u8 interface;
u8 revision;
u8 bist;
u8 header_type;
u8 latency_timer;
u8 cache_line_size;
u32 bar[2];
u8 primary_bus;
u8 secondary_bus;
u8 subordinate_bus;
u8 secondary_latency_timer;
u8 iobase;
u8 iolimit;
u16 secondary_status;
u16 membase;
u16 memlimit;
u16 iobaseupper;
u16 iolimitupper;
u32 romaddr;
u8 intline;
u8 intpin;
u16 bridgectrl;
/* PCI express capability */
u32 pcie_sltcap;
u16 pcie_devctl;
u16 pcie_rtctl;
};
struct mvebu_pcie_port;
/* Structure representing all PCIe interfaces */
struct mvebu_pcie {
struct platform_device *pdev;
struct mvebu_pcie_port *ports;