summaryrefslogtreecommitdiff
path: root/drivers/pci/controller/plda/pcie-microchip-host.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/pci/controller/plda/pcie-microchip-host.c')
-rw-r--r--drivers/pci/controller/plda/pcie-microchip-host.c222
1 files changed, 163 insertions, 59 deletions
diff --git a/drivers/pci/controller/plda/pcie-microchip-host.c b/drivers/pci/controller/plda/pcie-microchip-host.c
index 48f60a04b740..3fdfffdf0270 100644
--- a/drivers/pci/controller/plda/pcie-microchip-host.c
+++ b/drivers/pci/controller/plda/pcie-microchip-host.c
@@ -7,27 +7,31 @@
* Author: Daire McNamara <daire.mcnamara@microchip.com>
*/
+#include <linux/align.h>
+#include <linux/bits.h>
#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/irqchip/chained_irq.h>
#include <linux/irqdomain.h>
+#include <linux/log2.h>
#include <linux/module.h>
#include <linux/msi.h>
#include <linux/of_address.h>
#include <linux/of_pci.h>
#include <linux/pci-ecam.h>
#include <linux/platform_device.h>
+#include <linux/wordpart.h>
#include "../../pci.h"
#include "pcie-plda.h"
+#define MC_MAX_NUM_INBOUND_WINDOWS 8
+#define MPFS_NC_BOUNCE_ADDR 0x80000000
+
/* PCIe Bridge Phy and Controller Phy offsets */
#define MC_PCIE1_BRIDGE_ADDR 0x00008000u
#define MC_PCIE1_CTRL_ADDR 0x0000a000u
-#define MC_PCIE_BRIDGE_ADDR (MC_PCIE1_BRIDGE_ADDR)
-#define MC_PCIE_CTRL_ADDR (MC_PCIE1_CTRL_ADDR)
-
/* PCIe Controller Phy Regs */
#define SEC_ERROR_EVENT_CNT 0x20
#define DED_ERROR_EVENT_CNT 0x24
@@ -128,7 +132,6 @@
[EVENT_LOCAL_ ## x] = { __stringify(x), s }
#define PCIE_EVENT(x) \
- .base = MC_PCIE_CTRL_ADDR, \
.offset = PCIE_EVENT_INT, \
.mask_offset = PCIE_EVENT_INT, \
.mask_high = 1, \
@@ -136,7 +139,6 @@
.enb_mask = PCIE_EVENT_INT_ENB_MASK
#define SEC_EVENT(x) \
- .base = MC_PCIE_CTRL_ADDR, \
.offset = SEC_ERROR_INT, \
.mask_offset = SEC_ERROR_INT_MASK, \
.mask = SEC_ERROR_INT_ ## x ## _INT, \
@@ -144,7 +146,6 @@
.enb_mask = 0
#define DED_EVENT(x) \
- .base = MC_PCIE_CTRL_ADDR, \
.offset = DED_ERROR_INT, \
.mask_offset = DED_ERROR_INT_MASK, \
.mask_high = 1, \
@@ -152,7 +153,6 @@
.enb_mask = 0
#define LOCAL_EVENT(x) \
- .base = MC_PCIE_BRIDGE_ADDR, \
.offset = ISTATUS_LOCAL, \
.mask_offset = IMASK_LOCAL, \
.mask_high = 0, \
@@ -179,7 +179,8 @@ struct event_map {
struct mc_pcie {
struct plda_pcie_rp plda;
- void __iomem *axi_base_addr;
+ void __iomem *bridge_base_addr;
+ void __iomem *ctrl_base_addr;
};
struct cause {
@@ -253,7 +254,6 @@ static struct event_map local_status_to_event[] = {
};
static struct {
- u32 base;
u32 offset;
u32 mask;
u32 shift;
@@ -325,8 +325,7 @@ static inline u32 reg_to_event(u32 reg, struct event_map field)
static u32 pcie_events(struct mc_pcie *port)
{
- void __iomem *ctrl_base_addr = port->axi_base_addr + MC_PCIE_CTRL_ADDR;
- u32 reg = readl_relaxed(ctrl_base_addr + PCIE_EVENT_INT);
+ u32 reg = readl_relaxed(port->ctrl_base_addr + PCIE_EVENT_INT);
u32 val = 0;
int i;
@@ -338,8 +337,7 @@ static u32 pcie_events(struct mc_pcie *port)
static u32 sec_errors(struct mc_pcie *port)
{
- void __iomem *ctrl_base_addr = port->axi_base_addr + MC_PCIE_CTRL_ADDR;
- u32 reg = readl_relaxed(ctrl_base_addr + SEC_ERROR_INT);
+ u32 reg = readl_relaxed(port->ctrl_base_addr + SEC_ERROR_INT);
u32 val = 0;
int i;
@@ -351,8 +349,7 @@ static u32 sec_errors(struct mc_pcie *port)
static u32 ded_errors(struct mc_pcie *port)
{
- void __iomem *ctrl_base_addr = port->axi_base_addr + MC_PCIE_CTRL_ADDR;
- u32 reg = readl_relaxed(ctrl_base_addr + DED_ERROR_INT);
+ u32 reg = readl_relaxed(port->ctrl_base_addr + DED_ERROR_INT);
u32 val = 0;
int i;
@@ -364,8 +361,7 @@ static u32 ded_errors(struct mc_pcie *port)
static u32 local_events(struct mc_pcie *port)
{
- void __iomem *bridge_base_addr = port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
- u32 reg = readl_relaxed(bridge_base_addr + ISTATUS_LOCAL);
+ u32 reg = readl_relaxed(port->bridge_base_addr + ISTATUS_LOCAL);
u32 val = 0;
int i;
@@ -412,8 +408,12 @@ static void mc_ack_event_irq(struct irq_data *data)
void __iomem *addr;
u32 mask;
- addr = mc_port->axi_base_addr + event_descs[event].base +
- event_descs[event].offset;
+ if (event_descs[event].offset == ISTATUS_LOCAL)
+ addr = mc_port->bridge_base_addr;
+ else
+ addr = mc_port->ctrl_base_addr;
+
+ addr += event_descs[event].offset;
mask = event_descs[event].mask;
mask |= event_descs[event].enb_mask;
@@ -429,8 +429,12 @@ static void mc_mask_event_irq(struct irq_data *data)
u32 mask;
u32 val;
- addr = mc_port->axi_base_addr + event_descs[event].base +
- event_descs[event].mask_offset;
+ if (event_descs[event].offset == ISTATUS_LOCAL)
+ addr = mc_port->bridge_base_addr;
+ else
+ addr = mc_port->ctrl_base_addr;
+
+ addr += event_descs[event].mask_offset;
mask = event_descs[event].mask;
if (event_descs[event].enb_mask) {
mask <<= PCIE_EVENT_INT_ENB_SHIFT;
@@ -460,8 +464,12 @@ static void mc_unmask_event_irq(struct irq_data *data)
u32 mask;
u32 val;
- addr = mc_port->axi_base_addr + event_descs[event].base +
- event_descs[event].mask_offset;
+ if (event_descs[event].offset == ISTATUS_LOCAL)
+ addr = mc_port->bridge_base_addr;
+ else
+ addr = mc_port->ctrl_base_addr;
+
+ addr += event_descs[event].mask_offset;
mask = event_descs[event].mask;
if (event_descs[event].enb_mask)
@@ -554,26 +562,20 @@ static const struct plda_event mc_event = {
static inline void mc_clear_secs(struct mc_pcie *port)
{
- void __iomem *ctrl_base_addr = port->axi_base_addr + MC_PCIE_CTRL_ADDR;
-
- writel_relaxed(SEC_ERROR_INT_ALL_RAM_SEC_ERR_INT, ctrl_base_addr +
- SEC_ERROR_INT);
- writel_relaxed(0, ctrl_base_addr + SEC_ERROR_EVENT_CNT);
+ writel_relaxed(SEC_ERROR_INT_ALL_RAM_SEC_ERR_INT,
+ port->ctrl_base_addr + SEC_ERROR_INT);
+ writel_relaxed(0, port->ctrl_base_addr + SEC_ERROR_EVENT_CNT);
}
static inline void mc_clear_deds(struct mc_pcie *port)
{
- void __iomem *ctrl_base_addr = port->axi_base_addr + MC_PCIE_CTRL_ADDR;
-
- writel_relaxed(DED_ERROR_INT_ALL_RAM_DED_ERR_INT, ctrl_base_addr +
- DED_ERROR_INT);
- writel_relaxed(0, ctrl_base_addr + DED_ERROR_EVENT_CNT);
+ writel_relaxed(DED_ERROR_INT_ALL_RAM_DED_ERR_INT,
+ port->ctrl_base_addr + DED_ERROR_INT);
+ writel_relaxed(0, port->ctrl_base_addr + DED_ERROR_EVENT_CNT);
}
static void mc_disable_interrupts(struct mc_pcie *port)
{
- void __iomem *bridge_base_addr = port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
- void __iomem *ctrl_base_addr = port->axi_base_addr + MC_PCIE_CTRL_ADDR;
u32 val;
/* Ensure ECC bypass is enabled */
@@ -581,22 +583,22 @@ static void mc_disable_interrupts(struct mc_pcie *port)
ECC_CONTROL_RX_RAM_ECC_BYPASS |
ECC_CONTROL_PCIE2AXI_RAM_ECC_BYPASS |
ECC_CONTROL_AXI2PCIE_RAM_ECC_BYPASS;
- writel_relaxed(val, ctrl_base_addr + ECC_CONTROL);
+ writel_relaxed(val, port->ctrl_base_addr + ECC_CONTROL);
/* Disable SEC errors and clear any outstanding */
- writel_relaxed(SEC_ERROR_INT_ALL_RAM_SEC_ERR_INT, ctrl_base_addr +
- SEC_ERROR_INT_MASK);
+ writel_relaxed(SEC_ERROR_INT_ALL_RAM_SEC_ERR_INT,
+ port->ctrl_base_addr + SEC_ERROR_INT_MASK);
mc_clear_secs(port);
/* Disable DED errors and clear any outstanding */
- writel_relaxed(DED_ERROR_INT_ALL_RAM_DED_ERR_INT, ctrl_base_addr +
- DED_ERROR_INT_MASK);
+ writel_relaxed(DED_ERROR_INT_ALL_RAM_DED_ERR_INT,
+ port->ctrl_base_addr + DED_ERROR_INT_MASK);
mc_clear_deds(port);
/* Disable local interrupts and clear any outstanding */
- writel_relaxed(0, bridge_base_addr + IMASK_LOCAL);
- writel_relaxed(GENMASK(31, 0), bridge_base_addr + ISTATUS_LOCAL);
- writel_relaxed(GENMASK(31, 0), bridge_base_addr + ISTATUS_MSI);
+ writel_relaxed(0, port->bridge_base_addr + IMASK_LOCAL);
+ writel_relaxed(GENMASK(31, 0), port->bridge_base_addr + ISTATUS_LOCAL);
+ writel_relaxed(GENMASK(31, 0), port->bridge_base_addr + ISTATUS_MSI);
/* Disable PCIe events and clear any outstanding */
val = PCIE_EVENT_INT_L2_EXIT_INT |
@@ -605,11 +607,96 @@ static void mc_disable_interrupts(struct mc_pcie *port)
PCIE_EVENT_INT_L2_EXIT_INT_MASK |
PCIE_EVENT_INT_HOTRST_EXIT_INT_MASK |
PCIE_EVENT_INT_DLUP_EXIT_INT_MASK;
- writel_relaxed(val, ctrl_base_addr + PCIE_EVENT_INT);
+ writel_relaxed(val, port->ctrl_base_addr + PCIE_EVENT_INT);
/* Disable host interrupts and clear any outstanding */
- writel_relaxed(0, bridge_base_addr + IMASK_HOST);
- writel_relaxed(GENMASK(31, 0), bridge_base_addr + ISTATUS_HOST);
+ writel_relaxed(0, port->bridge_base_addr + IMASK_HOST);
+ writel_relaxed(GENMASK(31, 0), port->bridge_base_addr + ISTATUS_HOST);
+}
+
+static void mc_pcie_setup_inbound_atr(struct mc_pcie *port, int window_index,
+ u64 axi_addr, u64 pcie_addr, u64 size)
+{
+ u32 table_offset = window_index * ATR_ENTRY_SIZE;
+ void __iomem *table_addr = port->bridge_base_addr + table_offset;
+ u32 atr_sz;
+ u32 val;
+
+ atr_sz = ilog2(size) - 1;
+
+ val = ALIGN_DOWN(lower_32_bits(pcie_addr), SZ_4K);
+ val |= FIELD_PREP(ATR_SIZE_MASK, atr_sz);
+ val |= ATR_IMPL_ENABLE;
+
+ writel(val, table_addr + ATR0_PCIE_WIN0_SRCADDR_PARAM);
+
+ writel(upper_32_bits(pcie_addr), table_addr + ATR0_PCIE_WIN0_SRC_ADDR);
+
+ writel(lower_32_bits(axi_addr), table_addr + ATR0_PCIE_WIN0_TRSL_ADDR_LSB);
+ writel(upper_32_bits(axi_addr), table_addr + ATR0_PCIE_WIN0_TRSL_ADDR_UDW);
+
+ writel(TRSL_ID_AXI4_MASTER_0, table_addr + ATR0_PCIE_WIN0_TRSL_PARAM);
+}
+
+static int mc_pcie_setup_inbound_ranges(struct platform_device *pdev,
+ struct mc_pcie *port)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *dn = dev->of_node;
+ struct of_range_parser parser;
+ struct of_range range;
+ int atr_index = 0;
+
+ /*
+ * MPFS PCIe Root Port is 32-bit only, behind a Fabric Interface
+ * Controller FPGA logic block which contains the AXI-S interface.
+ *
+ * From the point of view of the PCIe Root Port, there are only two
+ * supported Root Port configurations:
+ *
+ * Configuration 1: for use with fully coherent designs; supports a
+ * window from 0x0 (CPU space) to specified PCIe space.
+ *
+ * Configuration 2: for use with non-coherent designs; supports two
+ * 1 GB windows to CPU space; one mapping CPU space 0 to PCIe space
+ * 0x80000000 and a second mapping CPU space 0x40000000 to PCIe
+ * space 0xc0000000. This cfg needs two windows because of how the
+ * MSI space is allocated in the AXI-S range on MPFS.
+ *
+ * The FIC interface outside the PCIe block *must* complete the
+ * inbound address translation as per MCHP MPFS FPGA design
+ * guidelines.
+ */
+ if (device_property_read_bool(dev, "dma-noncoherent")) {
+ /*
+ * Always need same two tables in this case. Need two tables
+ * due to hardware interactions between address and size.
+ */
+ mc_pcie_setup_inbound_atr(port, 0, 0,
+ MPFS_NC_BOUNCE_ADDR, SZ_1G);
+ mc_pcie_setup_inbound_atr(port, 1, SZ_1G,
+ MPFS_NC_BOUNCE_ADDR + SZ_1G, SZ_1G);
+ } else {
+ /* Find any DMA ranges */
+ if (of_pci_dma_range_parser_init(&parser, dn)) {
+ /* No DMA range property - setup default */
+ mc_pcie_setup_inbound_atr(port, 0, 0, 0, SZ_4G);
+ return 0;
+ }
+
+ for_each_of_range(&parser, &range) {
+ if (atr_index >= MC_MAX_NUM_INBOUND_WINDOWS) {
+ dev_err(dev, "too many inbound ranges; %d available tables\n",
+ MC_MAX_NUM_INBOUND_WINDOWS);
+ return -EINVAL;
+ }
+ mc_pcie_setup_inbound_atr(port, atr_index, 0,
+ range.pci_addr, range.size);
+ atr_index++;
+ }
+ }
+
+ return 0;
}
static int mc_platform_init(struct pci_config_window *cfg)
@@ -617,12 +704,10 @@ static int mc_platform_init(struct pci_config_window *cfg)
struct device *dev = cfg->parent;
struct platform_device *pdev = to_platform_device(dev);
struct pci_host_bridge *bridge = platform_get_drvdata(pdev);
- void __iomem *bridge_base_addr =
- port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
int ret;
/* Configure address translation table 0 for PCIe config space */
- plda_pcie_setup_window(bridge_base_addr, 0, cfg->res.start,
+ plda_pcie_setup_window(port->bridge_base_addr, 0, cfg->res.start,
cfg->res.start,
resource_size(&cfg->res));
@@ -634,6 +719,10 @@ static int mc_platform_init(struct pci_config_window *cfg)
if (ret)
return ret;
+ ret = mc_pcie_setup_inbound_ranges(pdev, port);
+ if (ret)
+ return ret;
+
port->plda.event_ops = &mc_event_ops;
port->plda.event_irq_chip = &mc_event_irq_chip;
port->plda.events_bitmap = GENMASK(NUM_EVENTS - 1, 0);
@@ -649,7 +738,7 @@ static int mc_platform_init(struct pci_config_window *cfg)
static int mc_host_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- void __iomem *bridge_base_addr;
+ void __iomem *apb_base_addr;
struct plda_pcie_rp *plda;
int ret;
u32 val;
@@ -661,30 +750,45 @@ static int mc_host_probe(struct platform_device *pdev)
plda = &port->plda;
plda->dev = dev;
- port->axi_base_addr = devm_platform_ioremap_resource(pdev, 1);
- if (IS_ERR(port->axi_base_addr))
- return PTR_ERR(port->axi_base_addr);
+ port->bridge_base_addr = devm_platform_ioremap_resource_byname(pdev,
+ "bridge");
+ port->ctrl_base_addr = devm_platform_ioremap_resource_byname(pdev,
+ "ctrl");
+ if (!IS_ERR(port->bridge_base_addr) && !IS_ERR(port->ctrl_base_addr))
+ goto addrs_set;
+
+ /*
+ * The original, incorrect, binding that lumped the control and
+ * bridge addresses together still needs to be handled by the driver.
+ */
+ apb_base_addr = devm_platform_ioremap_resource_byname(pdev, "apb");
+ if (IS_ERR(apb_base_addr))
+ return dev_err_probe(dev, PTR_ERR(apb_base_addr),
+ "both legacy apb register and ctrl/bridge regions missing");
+
+ port->bridge_base_addr = apb_base_addr + MC_PCIE1_BRIDGE_ADDR;
+ port->ctrl_base_addr = apb_base_addr + MC_PCIE1_CTRL_ADDR;
+addrs_set:
mc_disable_interrupts(port);
- bridge_base_addr = port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
- plda->bridge_addr = bridge_base_addr;
+ plda->bridge_addr = port->bridge_base_addr;
plda->num_events = NUM_EVENTS;
/* Allow enabling MSI by disabling MSI-X */
- val = readl(bridge_base_addr + PCIE_PCI_IRQ_DW0);
+ val = readl(port->bridge_base_addr + PCIE_PCI_IRQ_DW0);
val &= ~MSIX_CAP_MASK;
- writel(val, bridge_base_addr + PCIE_PCI_IRQ_DW0);
+ writel(val, port->bridge_base_addr + PCIE_PCI_IRQ_DW0);
/* Pick num vectors from bitfile programmed onto FPGA fabric */
- val = readl(bridge_base_addr + PCIE_PCI_IRQ_DW0);
+ val = readl(port->bridge_base_addr + PCIE_PCI_IRQ_DW0);
val &= NUM_MSI_MSGS_MASK;
val >>= NUM_MSI_MSGS_SHIFT;
plda->msi.num_vectors = 1 << val;
/* Pick vector address from design */
- plda->msi.vector_phy = readl_relaxed(bridge_base_addr + IMSI_ADDR);
+ plda->msi.vector_phy = readl_relaxed(port->bridge_base_addr + IMSI_ADDR);
ret = mc_pcie_init_clks(dev);
if (ret) {