Merge branch 'pci/controller/mediatek'

- Use FIELD_PREP() to fix incorrect operator precedence in PCIE_FTS_NUM_L0
  (Li RongQing)

- Fix IRQ domain leak when port fails to enable (Manivannan Sadhasivam)

- Use actual physical address for MSI message address instead of
  virt_to_phys() (Manivannan Sadhasivam)

- Add EcoNet EN7528 to DT binding (Caleb James DeLisle)

* pci/controller/mediatek:
  dt-bindings: PCI: mediatek: Add support for EcoNet EN7528
  PCI: mediatek: Use actual physical address instead of virt_to_phys()
  PCI: mediatek: Fix IRQ domain leak when port fails to enable
  PCI: mediatek: Fix operator precedence in PCIE_FTS_NUM_L0 macro
This commit is contained in:
Bjorn Helgaas 2026-06-23 17:32:16 -05:00
commit 7b90019012
2 changed files with 82 additions and 28 deletions

View File

@ -14,6 +14,7 @@ properties:
oneOf:
- enum:
- airoha,an7583-pcie
- econet,en7528-pcie
- mediatek,mt2712-pcie
- mediatek,mt7622-pcie
- mediatek,mt7629-pcie
@ -226,6 +227,31 @@ allOf:
mediatek,pbus-csr: false
- if:
properties:
compatible:
contains:
const: econet,en7528-pcie
then:
properties:
clocks:
maxItems: 1
clock-names:
maxItems: 1
resets: false
reset-names: false
power-domains: false
mediatek,pbus-csr: false
required:
- phys
- phy-names
unevaluatedProperties: false
examples:

View File

@ -7,6 +7,7 @@
* Honghui Zhang <honghui.zhang@mediatek.com>
*/
#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/iopoll.h>
@ -61,7 +62,7 @@
/* MediaTek specific configuration registers */
#define PCIE_FTS_NUM 0x70c
#define PCIE_FTS_NUM_MASK GENMASK(15, 8)
#define PCIE_FTS_NUM_L0(x) ((x) & 0xff << 8)
#define PCIE_FTS_NUM_L0(x) FIELD_PREP(PCIE_FTS_NUM_MASK, x)
#define PCIE_FC_CREDIT 0x73c
#define PCIE_FC_CREDIT_MASK (GENMASK(31, 31) | GENMASK(28, 16))
@ -175,6 +176,7 @@ struct mtk_pcie_soc {
/**
* struct mtk_pcie_port - PCIe port information
* @base: IO mapped register base
* @phys_base: Physical address of the I/O register base region
* @list: port list
* @pcie: pointer to PCIe host info
* @reset: pointer to port reset control
@ -196,6 +198,7 @@ struct mtk_pcie_soc {
*/
struct mtk_pcie_port {
void __iomem *base;
phys_addr_t phys_base;
struct list_head list;
struct mtk_pcie *pcie;
struct reset_control *reset;
@ -405,7 +408,7 @@ static void mtk_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
phys_addr_t addr;
/* MT2712/MT7622 only support 32-bit MSI addresses */
addr = virt_to_phys(port->base + PCIE_MSI_VECTOR);
addr = port->phys_base + PCIE_MSI_VECTOR;
msg->address_hi = 0;
msg->address_lo = lower_32_bits(addr);
@ -520,7 +523,7 @@ static void mtk_pcie_enable_msi(struct mtk_pcie_port *port)
u32 val;
phys_addr_t msg_addr;
msg_addr = virt_to_phys(port->base + PCIE_MSI_VECTOR);
msg_addr = port->phys_base + PCIE_MSI_VECTOR;
val = lower_32_bits(msg_addr);
writel(val, port->base + PCIE_IMSI_ADDR);
@ -529,23 +532,27 @@ static void mtk_pcie_enable_msi(struct mtk_pcie_port *port)
writel(val, port->base + PCIE_INT_MASK);
}
static void mtk_pcie_irq_teardown_port(struct mtk_pcie_port *port)
{
irq_set_chained_handler_and_data(port->irq, NULL, NULL);
if (port->irq_domain)
irq_domain_remove(port->irq_domain);
if (IS_ENABLED(CONFIG_PCI_MSI)) {
if (port->inner_domain)
irq_domain_remove(port->inner_domain);
}
irq_dispose_mapping(port->irq);
}
static void mtk_pcie_irq_teardown(struct mtk_pcie *pcie)
{
struct mtk_pcie_port *port, *tmp;
list_for_each_entry_safe(port, tmp, &pcie->ports, list) {
irq_set_chained_handler_and_data(port->irq, NULL, NULL);
if (port->irq_domain)
irq_domain_remove(port->irq_domain);
if (IS_ENABLED(CONFIG_PCI_MSI)) {
if (port->inner_domain)
irq_domain_remove(port->inner_domain);
}
irq_dispose_mapping(port->irq);
}
list_for_each_entry_safe(port, tmp, &pcie->ports, list)
mtk_pcie_irq_teardown_port(port);
}
static int mtk_pcie_intx_map(struct irq_domain *domain, unsigned int irq,
@ -865,7 +872,7 @@ static int mtk_pcie_startup_port_an7583(struct mtk_pcie_port *port)
return mtk_pcie_startup_port_v2(port);
}
static void mtk_pcie_enable_port(struct mtk_pcie_port *port)
static int mtk_pcie_enable_port(struct mtk_pcie_port *port)
{
struct mtk_pcie *pcie = port->pcie;
struct device *dev = pcie->dev;
@ -874,7 +881,7 @@ static void mtk_pcie_enable_port(struct mtk_pcie_port *port)
err = clk_prepare_enable(port->sys_ck);
if (err) {
dev_err(dev, "failed to enable sys_ck%d clock\n", port->slot);
goto err_sys_clk;
return err;
}
err = clk_prepare_enable(port->ahb_ck);
@ -922,11 +929,15 @@ static void mtk_pcie_enable_port(struct mtk_pcie_port *port)
goto err_phy_on;
}
if (!pcie->soc->startup(port))
return;
err = pcie->soc->startup(port);
if (err) {
dev_info(dev, "Port%d link down\n", port->slot);
goto err_soc_startup;
}
dev_info(dev, "Port%d link down\n", port->slot);
return 0;
err_soc_startup:
phy_power_off(port->phy);
err_phy_on:
phy_exit(port->phy);
@ -942,8 +953,8 @@ static void mtk_pcie_enable_port(struct mtk_pcie_port *port)
clk_disable_unprepare(port->ahb_ck);
err_ahb_clk:
clk_disable_unprepare(port->sys_ck);
err_sys_clk:
mtk_pcie_port_free(port);
return err;
}
static int mtk_pcie_parse_port(struct mtk_pcie *pcie,
@ -953,6 +964,7 @@ static int mtk_pcie_parse_port(struct mtk_pcie *pcie,
struct mtk_pcie_port *port;
struct device *dev = pcie->dev;
struct platform_device *pdev = to_platform_device(dev);
struct resource *res;
char name[20];
int err;
@ -961,7 +973,14 @@ static int mtk_pcie_parse_port(struct mtk_pcie *pcie,
return -ENOMEM;
snprintf(name, sizeof(name), "port%d", slot);
port->base = devm_platform_ioremap_resource_byname(pdev, name);
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name);
if (!res) {
dev_err(dev, "failed to get port%d base\n", slot);
return -EINVAL;
}
port->phys_base = res->start;
port->base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(port->base)) {
dev_err(dev, "failed to map port%d base\n", slot);
return PTR_ERR(port->base);
@ -1109,8 +1128,13 @@ static int mtk_pcie_setup(struct mtk_pcie *pcie)
return err;
/* enable each port, and then check link status */
list_for_each_entry_safe(port, tmp, &pcie->ports, list)
mtk_pcie_enable_port(port);
list_for_each_entry_safe(port, tmp, &pcie->ports, list) {
err = mtk_pcie_enable_port(port);
if (err) {
mtk_pcie_irq_teardown_port(port);
mtk_pcie_port_free(port);
}
}
/* power down PCIe subsys if slots are all empty (link down) */
if (list_empty(&pcie->ports))
@ -1209,14 +1233,18 @@ static int mtk_pcie_resume_noirq(struct device *dev)
{
struct mtk_pcie *pcie = dev_get_drvdata(dev);
struct mtk_pcie_port *port, *tmp;
int err;
if (list_empty(&pcie->ports))
return 0;
clk_prepare_enable(pcie->free_ck);
list_for_each_entry_safe(port, tmp, &pcie->ports, list)
mtk_pcie_enable_port(port);
list_for_each_entry_safe(port, tmp, &pcie->ports, list) {
err = mtk_pcie_enable_port(port);
if (err)
mtk_pcie_port_free(port);
}
/* In case of EP was removed while system suspend. */
if (list_empty(&pcie->ports))