mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-31 14:04:27 -04:00
Merge branch 'pci/controller/mediatek'
- Add support for PCIe controller in EcoNet EN7528 and EN751221 SoCs (Caleb James DeLisle) * pci/controller/mediatek: PCI: mediatek: Add support for EcoNet EN7528 SoC
This commit is contained in:
@@ -209,7 +209,7 @@ config PCI_MVEBU
|
||||
|
||||
config PCIE_MEDIATEK
|
||||
tristate "MediaTek PCIe controller"
|
||||
depends on ARCH_AIROHA || ARCH_MEDIATEK || COMPILE_TEST
|
||||
depends on ARCH_AIROHA || ARCH_MEDIATEK || ECONET || COMPILE_TEST
|
||||
depends on OF
|
||||
depends on PCI_MSI
|
||||
select IRQ_MSI_LIB
|
||||
|
||||
@@ -10,11 +10,13 @@
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/irqchip/chained_irq.h>
|
||||
#include <linux/irqchip/irq-msi-lib.h>
|
||||
#include <linux/irqdomain.h>
|
||||
#include <linux/kconfig.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/msi.h>
|
||||
@@ -78,6 +80,7 @@
|
||||
|
||||
#define PCIE_CONF_VEND_ID 0x100
|
||||
#define PCIE_CONF_DEVICE_ID 0x102
|
||||
#define PCIE_CONF_REV_CLASS 0x104
|
||||
#define PCIE_CONF_CLASS_ID 0x106
|
||||
|
||||
#define PCIE_INT_MASK 0x420
|
||||
@@ -90,6 +93,11 @@
|
||||
#define MSI_MASK BIT(23)
|
||||
#define MTK_MSI_IRQS_NUM 32
|
||||
|
||||
#define EN7528_HOST_MODE 0x00804201
|
||||
#define EN7528_LINKUP_REG 0x50
|
||||
#define EN7528_RC0_LINKUP BIT(1)
|
||||
#define EN7528_RC1_LINKUP BIT(2)
|
||||
|
||||
#define PCIE_AHB_TRANS_BASE0_L 0x438
|
||||
#define PCIE_AHB_TRANS_BASE0_H 0x43c
|
||||
#define AHB2PCIE_SIZE(x) ((x) & GENMASK(4, 0))
|
||||
@@ -145,12 +153,15 @@ struct mtk_pcie_port;
|
||||
* @MTK_PCIE_FIX_DEVICE_ID: host's device ID needed to be fixed
|
||||
* @MTK_PCIE_NO_MSI: Bridge has no MSI support, and relies on an external block
|
||||
* @MTK_PCIE_SKIP_RSTB: Skip calling RSTB bits on PCIe probe
|
||||
* @MTK_PCIE_RETRAIN: Retrain link to bridge after startup because some
|
||||
* Gen2-capable devices start as Gen1.
|
||||
*/
|
||||
enum mtk_pcie_quirks {
|
||||
MTK_PCIE_FIX_CLASS_ID = BIT(0),
|
||||
MTK_PCIE_FIX_DEVICE_ID = BIT(1),
|
||||
MTK_PCIE_NO_MSI = BIT(2),
|
||||
MTK_PCIE_SKIP_RSTB = BIT(3),
|
||||
MTK_PCIE_RETRAIN = BIT(4),
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -756,6 +767,134 @@ static int mtk_pcie_startup_port_v2(struct mtk_pcie_port *port)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mtk_pcie_startup_port_en7528(struct mtk_pcie_port *port)
|
||||
{
|
||||
struct mtk_pcie *pcie = port->pcie;
|
||||
struct pci_host_bridge *host = pci_host_bridge_from_priv(pcie);
|
||||
struct resource *mem = NULL;
|
||||
struct resource_entry *entry;
|
||||
u32 val, link_mask;
|
||||
int err;
|
||||
|
||||
entry = resource_list_first_type(&host->windows, IORESOURCE_MEM);
|
||||
if (entry)
|
||||
mem = entry->res;
|
||||
if (!mem)
|
||||
return -EINVAL;
|
||||
|
||||
if (!pcie->cfg) {
|
||||
dev_err(pcie->dev, "EN7528: pciecfg syscon not available\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Assert all reset signals */
|
||||
writel(0, port->base + PCIE_RST_CTRL);
|
||||
|
||||
/*
|
||||
* Enable PCIe link down reset, if link status changed from link up to
|
||||
* link down, this will reset MAC control registers and configuration
|
||||
* space.
|
||||
*/
|
||||
writel(PCIE_LINKDOWN_RST_EN, port->base + PCIE_RST_CTRL);
|
||||
|
||||
msleep(PCIE_T_PVPERL_MS);
|
||||
|
||||
/* De-assert PHY, PE, PIPE, MAC and configuration reset */
|
||||
val = readl(port->base + PCIE_RST_CTRL);
|
||||
val |= PCIE_PHY_RSTB | PCIE_PERSTB | PCIE_PIPE_SRSTB |
|
||||
PCIE_MAC_SRSTB | PCIE_CRSTB;
|
||||
writel(val, port->base + PCIE_RST_CTRL);
|
||||
|
||||
writel(PCIE_CLASS_CODE | PCIE_REVISION_ID,
|
||||
port->base + PCIE_CONF_REV_CLASS);
|
||||
writel(EN7528_HOST_MODE, port->base);
|
||||
|
||||
link_mask = (port->slot == 0) ? EN7528_RC0_LINKUP : EN7528_RC1_LINKUP;
|
||||
|
||||
/* 100ms timeout value should be enough for Gen1/2 training */
|
||||
err = regmap_read_poll_timeout(pcie->cfg, EN7528_LINKUP_REG, val,
|
||||
!!(val & link_mask), 20,
|
||||
PCI_PM_D3COLD_WAIT * USEC_PER_MSEC);
|
||||
if (err) {
|
||||
dev_err(pcie->dev, "EN7528: port%d link timeout\n", port->slot);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
/* Activate INTx interrupts */
|
||||
val = readl(port->base + PCIE_INT_MASK);
|
||||
val &= ~INTX_MASK;
|
||||
writel(val, port->base + PCIE_INT_MASK);
|
||||
|
||||
if (IS_ENABLED(CONFIG_PCI_MSI))
|
||||
mtk_pcie_enable_msi(port);
|
||||
|
||||
/* Set AHB to PCIe translation windows */
|
||||
val = lower_32_bits(mem->start) |
|
||||
AHB2PCIE_SIZE(fls(resource_size(mem)));
|
||||
writel(val, port->base + PCIE_AHB_TRANS_BASE0_L);
|
||||
|
||||
val = upper_32_bits(mem->start);
|
||||
writel(val, port->base + PCIE_AHB_TRANS_BASE0_H);
|
||||
|
||||
writel(WIN_ENABLE, port->base + PCIE_AXI_WINDOW0);
|
||||
|
||||
if (!IS_BUILTIN(CONFIG_PCIE_MEDIATEK))
|
||||
dev_info(pcie->dev,
|
||||
"module not built-in, Gen2 unavailable even if supported\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* mtk_pcie_retrain - retrain the root bridge link if needed
|
||||
* @dev: The device, for use in logging
|
||||
* @host: The host bridge which contains the link
|
||||
*
|
||||
* Due to what is likely a hardware bug, some devices (notably EcoNet) start up
|
||||
* as Gen1, and must be retrained once after initial configuration in order to
|
||||
* reach Gen2.
|
||||
*
|
||||
* These devices always self-identify as Gen2 capable, but sometimes the PHY is
|
||||
* only capable of Gen1 operation, and sometimes the PCIe card (e.g. wifi) is
|
||||
* only Gen1 capable. Therefore it is most convenient to retrain every port
|
||||
* after startup.
|
||||
*/
|
||||
static int mtk_pcie_retrain(struct device *dev, struct pci_host_bridge *host)
|
||||
{
|
||||
struct pci_dev *rp;
|
||||
int ret = -ENOENT;
|
||||
u16 lnksta = 0;
|
||||
u32 speed;
|
||||
|
||||
/* Should already have been warned about during startup_port */
|
||||
if (!IS_BUILTIN(CONFIG_PCIE_MEDIATEK))
|
||||
return 0;
|
||||
|
||||
guard(rwsem_read)(&pci_bus_sem);
|
||||
|
||||
for_each_pci_bridge(rp, host->bus) {
|
||||
if (pci_pcie_type(rp) != PCI_EXP_TYPE_ROOT_PORT)
|
||||
continue;
|
||||
|
||||
#if IS_BUILTIN(CONFIG_PCIE_MEDIATEK)
|
||||
ret = pcie_retrain_link(rp, true);
|
||||
#endif
|
||||
|
||||
if (ret)
|
||||
return dev_err_probe(&rp->dev, ret,
|
||||
"failed to retrain port\n");
|
||||
|
||||
pcie_capability_read_word(rp, PCI_EXP_LNKSTA, &lnksta);
|
||||
speed = lnksta & PCI_EXP_LNKSTA_CLS;
|
||||
|
||||
pci_info(rp, "link retrained, speed %s\n",
|
||||
pci_speed_string(pcie_link_speed[speed]));
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __iomem *mtk_pcie_map_bus(struct pci_bus *bus,
|
||||
unsigned int devfn, int where)
|
||||
{
|
||||
@@ -1169,6 +1308,13 @@ static int mtk_pcie_probe(struct platform_device *pdev)
|
||||
if (err)
|
||||
goto put_resources;
|
||||
|
||||
/*
|
||||
* Ignore error because pci_host_probe() was already called, and in any
|
||||
* case it is possible that the port will still work as Gen1.
|
||||
*/
|
||||
if (pcie->soc->quirks & MTK_PCIE_RETRAIN)
|
||||
mtk_pcie_retrain(dev, host);
|
||||
|
||||
return 0;
|
||||
|
||||
put_resources:
|
||||
@@ -1290,8 +1436,16 @@ static const struct mtk_pcie_soc mtk_pcie_soc_mt7629 = {
|
||||
.quirks = MTK_PCIE_FIX_CLASS_ID | MTK_PCIE_FIX_DEVICE_ID,
|
||||
};
|
||||
|
||||
static const struct mtk_pcie_soc mtk_pcie_soc_en7528 = {
|
||||
.ops = &mtk_pcie_ops_v2,
|
||||
.startup = mtk_pcie_startup_port_en7528,
|
||||
.setup_irq = mtk_pcie_setup_irq,
|
||||
.quirks = MTK_PCIE_RETRAIN,
|
||||
};
|
||||
|
||||
static const struct of_device_id mtk_pcie_ids[] = {
|
||||
{ .compatible = "airoha,an7583-pcie", .data = &mtk_pcie_soc_an7583 },
|
||||
{ .compatible = "econet,en7528-pcie", .data = &mtk_pcie_soc_en7528 },
|
||||
{ .compatible = "mediatek,mt2701-pcie", .data = &mtk_pcie_soc_v1 },
|
||||
{ .compatible = "mediatek,mt7623-pcie", .data = &mtk_pcie_soc_v1 },
|
||||
{ .compatible = "mediatek,mt2712-pcie", .data = &mtk_pcie_soc_mt2712 },
|
||||
|
||||
Reference in New Issue
Block a user