mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-04-15 09:31:32 -04:00
Merge branch 'pci/controller/microchip'
- Set up the inbound address translation based on whether the platform allows coherent or non-coherent DMA (Daire McNamara) - Update DT binding such that platforms are DMA-coherent by default and must specify 'dma-noncoherent' if needed (Conor Dooley) * pci/controller/microchip: dt-bindings: PCI: microchip,pcie-host: Allow dma-noncoherent PCI: microchip: Set inbound address translation for coherent or non-coherent mode
This commit is contained in:
@@ -50,6 +50,8 @@ properties:
|
||||
items:
|
||||
pattern: '^fic[0-3]$'
|
||||
|
||||
dma-coherent: true
|
||||
|
||||
ranges:
|
||||
minItems: 1
|
||||
maxItems: 3
|
||||
|
||||
@@ -7,20 +7,27 @@
|
||||
* 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
|
||||
@@ -607,6 +614,91 @@ static void mc_disable_interrupts(struct mc_pcie *port)
|
||||
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)
|
||||
{
|
||||
struct device *dev = cfg->parent;
|
||||
@@ -627,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);
|
||||
|
||||
@@ -8,11 +8,14 @@
|
||||
* Author: Daire McNamara <daire.mcnamara@microchip.com>
|
||||
*/
|
||||
|
||||
#include <linux/align.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/irqchip/chained_irq.h>
|
||||
#include <linux/irqdomain.h>
|
||||
#include <linux/msi.h>
|
||||
#include <linux/pci_regs.h>
|
||||
#include <linux/pci-ecam.h>
|
||||
#include <linux/wordpart.h>
|
||||
|
||||
#include "pcie-plda.h"
|
||||
|
||||
@@ -502,8 +505,9 @@ void plda_pcie_setup_window(void __iomem *bridge_base_addr, u32 index,
|
||||
writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) +
|
||||
ATR0_AXI4_SLV0_TRSL_PARAM);
|
||||
|
||||
val = lower_32_bits(axi_addr) | (atr_sz << ATR_SIZE_SHIFT) |
|
||||
ATR_IMPL_ENABLE;
|
||||
val = ALIGN_DOWN(lower_32_bits(axi_addr), SZ_4K);
|
||||
val |= FIELD_PREP(ATR_SIZE_MASK, atr_sz);
|
||||
val |= ATR_IMPL_ENABLE;
|
||||
writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) +
|
||||
ATR0_AXI4_SLV0_SRCADDR_PARAM);
|
||||
|
||||
@@ -518,13 +522,20 @@ void plda_pcie_setup_window(void __iomem *bridge_base_addr, u32 index,
|
||||
val = upper_32_bits(pci_addr);
|
||||
writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) +
|
||||
ATR0_AXI4_SLV0_TRSL_ADDR_UDW);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(plda_pcie_setup_window);
|
||||
|
||||
void plda_pcie_setup_inbound_address_translation(struct plda_pcie_rp *port)
|
||||
{
|
||||
void __iomem *bridge_base_addr = port->bridge_addr;
|
||||
u32 val;
|
||||
|
||||
val = readl(bridge_base_addr + ATR0_PCIE_WIN0_SRCADDR_PARAM);
|
||||
val |= (ATR0_PCIE_ATR_SIZE << ATR0_PCIE_ATR_SIZE_SHIFT);
|
||||
writel(val, bridge_base_addr + ATR0_PCIE_WIN0_SRCADDR_PARAM);
|
||||
writel(0, bridge_base_addr + ATR0_PCIE_WIN0_SRC_ADDR);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(plda_pcie_setup_window);
|
||||
EXPORT_SYMBOL_GPL(plda_pcie_setup_inbound_address_translation);
|
||||
|
||||
int plda_pcie_setup_iomems(struct pci_host_bridge *bridge,
|
||||
struct plda_pcie_rp *port)
|
||||
|
||||
@@ -89,14 +89,15 @@
|
||||
|
||||
/* PCIe AXI slave table init defines */
|
||||
#define ATR0_AXI4_SLV0_SRCADDR_PARAM 0x800u
|
||||
#define ATR_SIZE_SHIFT 1
|
||||
#define ATR_IMPL_ENABLE 1
|
||||
#define ATR_SIZE_MASK GENMASK(6, 1)
|
||||
#define ATR_IMPL_ENABLE BIT(0)
|
||||
#define ATR0_AXI4_SLV0_SRC_ADDR 0x804u
|
||||
#define ATR0_AXI4_SLV0_TRSL_ADDR_LSB 0x808u
|
||||
#define ATR0_AXI4_SLV0_TRSL_ADDR_UDW 0x80cu
|
||||
#define ATR0_AXI4_SLV0_TRSL_PARAM 0x810u
|
||||
#define PCIE_TX_RX_INTERFACE 0x00000000u
|
||||
#define PCIE_CONFIG_INTERFACE 0x00000001u
|
||||
#define TRSL_ID_AXI4_MASTER_0 0x00000004u
|
||||
|
||||
#define CONFIG_SPACE_ADDR_OFFSET 0x1000u
|
||||
|
||||
@@ -204,6 +205,7 @@ int plda_init_interrupts(struct platform_device *pdev,
|
||||
void plda_pcie_setup_window(void __iomem *bridge_base_addr, u32 index,
|
||||
phys_addr_t axi_addr, phys_addr_t pci_addr,
|
||||
size_t size);
|
||||
void plda_pcie_setup_inbound_address_translation(struct plda_pcie_rp *port);
|
||||
int plda_pcie_setup_iomems(struct pci_host_bridge *bridge,
|
||||
struct plda_pcie_rp *port);
|
||||
int plda_pcie_host_init(struct plda_pcie_rp *port, struct pci_ops *ops,
|
||||
|
||||
Reference in New Issue
Block a user