From: Yueqiang Fan Subject: [PATCH] net: stmmac: dwmac-generic: add ACPI support for Phytium FT-2000/4 and D2000 The Phytium FT-2000/4 and D2000 SoCs integrate a DesignWare GMAC that is exposed to ACPI as "FTGM0001" / "PHYT0004". The generic dwmac platform driver only supports DT and legacy platform data, so on ACPI (UEFI) firmware the MACs are never probed and there is no network device. Add an ACPI device ID table plus a probe configuration path that mirrors the generic "snps,dwmac" DT defaults, using the values from the vendor (Kylin) firmware DTB for eth@2820c000 / eth@28210000: - phy-mode rgmii-txid. Both SoC MACs share the same two RTL8211F PHYs on a common MDIO wiring (each bus sees both PHYs at addr 0 and 7), so pin each MAC to its own PHY address instead of scanning: 0 for ACPI instance :00, 7 for :01. - single DMA channel (dwmac1000-compatible core): use one TX/RX queue. The netdev is always allocated with MTL_MAX_TX/RX_QUEUES queues, but the per-queue DMA arrays are only set up for tx/rx_queues_to_use queues and stmmac_xmit() does not guard queue >= tx_queues_to_use. With real_num_tx_queues set to 1, stmmac_select_queue() reduces every frame to queue 0 so the missing guard is never hit. - has_gmac (dwmac1000 hwif; the dwmac4 hwif fails MDIO on this core), AXI burst lengths 16/8/4, pbl 16 with fixed burst, FIFO sizes 0x1000. All values can be overridden through ACPI _DSD properties (phy-mode, phy-addr, tx/rx-queues-to-use, snps,pbl). Tested on a Phytium D2000 board (JWIPC IF24TH01) with Ubuntu 24.04 / kernel 6.8.0: both MACs probe (PHYT0004:00/01), interfaces enaphyt4i0/1 are created, and cable link-up + DHCP work with no transmit watchdog and no kernel panic. The default single-queue configuration avoids the NULL tx_skbuff dereference in stmmac_xmit() that the stock multi-queue defaults trigger on this hardware. Signed-off-by: Yueqiang Fan --- drivers/net/ethernet/stmicro/stmmac/dwmac-generic.c | 97 +++++++++++++ 1 file changed, 97 insertions(+) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-generic.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-generic.c --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-generic.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-generic.c @@ -12,10 +12,107 @@ #include #include #include +#include +#include +#include #include "stmmac.h" #include "stmmac_platform.h" +/* + * Phytium FT-2000/4 and D2000 SoCs integrate a DesignWare GMAC that is + * exposed to ACPI as "FTGM0001" / "PHYT0004". There is no upstream ACPI + * support for the generic dwmac platform driver, so provide the + * configuration that the generic "snps,dwmac" DT path would produce, + * with the values taken from the vendor (Kylin) firmware DTB for the + * eth@2820c000 / eth@28210000 nodes. + * + * Platform facts: + * - phy-mode is rgmii-txid; both MACs share the same two RTL8211F PHYs + * on a common MDIO wiring (each bus sees both PHYs, at addr 0 and 7), + * so pin each MAC to its own PHY address (0 for instance :00, 7 for + * :01) instead of scanning. + * - the MAC is a dwmac1000-compatible core with a single DMA channel: + * use one TX/RX queue. The netdev is always allocated with + * MTL_MAX_TX/RX_QUEUES queues but the per-queue DMA arrays are only + * set up for tx/rx_queues_to_use queues, and stmmac_xmit() does not + * guard queue >= tx_queues_to_use. With real_num_tx_queues set to 1, + * stmmac_select_queue() reduces every frame to queue 0, so the + * missing guard is never hit. + * - do not enable has_gmac4: despite dwmac4-style capability registers + * the MDIO registers are dwmac1000-compatible. + * + * All values may be overridden through ACPI _DSD properties. + */ +static int dwmac_generic_acpi_probe_config(struct platform_device *pdev, + struct plat_stmmacenet_data *plat) +{ + struct device *dev = &pdev->dev; + struct stmmac_dma_cfg *dma_cfg; + struct stmmac_axi *axi; + const char *phy_mode; + + plat->phy_interface = PHY_INTERFACE_MODE_RGMII_TXID; + if (!device_property_read_string(dev, "phy-mode", &phy_mode)) + phy_interface_mode_from_string(&plat->phy_interface, phy_mode); + + /* bus id from the ACPI instance: PHYT0004:00 -> 0, PHYT0004:01 -> 1 */ + plat->bus_id = 0; + { + const char *nm = dev_name(dev); + const char *sep = nm ? strrchr(nm, ':') : NULL; + int id; + + if (sep && !kstrtoint(sep + 1, 10, &id)) + plat->bus_id = id; + } + + plat->phy_addr = plat->bus_id ? 7 : 0; + device_property_read_u32(dev, "phy-addr", &plat->phy_addr); + + plat->clk_csr = -1; + plat->maxmtu = JUMBO_LEN; + plat->multicast_filter_bins = HASH_TABLE_SIZE; + plat->unicast_filter_entries = 128; + plat->has_gmac = 1; + plat->pmt = 1; + + plat->tx_queues_to_use = 1; + plat->rx_queues_to_use = 1; + device_property_read_u32(dev, "tx-queues-to-use", + &plat->tx_queues_to_use); + device_property_read_u32(dev, "rx-queues-to-use", + &plat->rx_queues_to_use); + + plat->force_sf_dma_mode = 1; + plat->tx_fifo_size = 0x1000; + plat->rx_fifo_size = 0x1000; + + plat->mdio_bus_data = devm_kzalloc(dev, sizeof(*plat->mdio_bus_data), + GFP_KERNEL); + if (!plat->mdio_bus_data) + return -ENOMEM; + plat->mdio_bus_data->phy_mask = 0; + + dma_cfg = devm_kzalloc(dev, sizeof(*dma_cfg), GFP_KERNEL); + if (!dma_cfg) + return -ENOMEM; + dma_cfg->pbl = 16; + dma_cfg->fixed_burst = 1; + device_property_read_u32(dev, "snps,pbl", &dma_cfg->pbl); + plat->dma_cfg = dma_cfg; + + axi = devm_kzalloc(dev, sizeof(*axi), GFP_KERNEL); + if (!axi) + return -ENOMEM; + axi->axi_blen[4] = 16; + axi->axi_blen[5] = 8; + axi->axi_blen[6] = 4; + plat->axi = axi; + + return 0; +} + static int dwmac_generic_probe(struct platform_device *pdev) { struct plat_stmmacenet_data *plat_dat; struct stmmac_resources stmmac_res; int ret; @@ -29,6 +126,15 @@ static int dwmac_generic_probe(struct platform_device *pdev) dev_err(&pdev->dev, "dt configuration failed\n"); return PTR_ERR(plat_dat); } + } else if (has_acpi_companion(&pdev->dev)) { + plat_dat = devm_kzalloc(&pdev->dev, sizeof(*plat_dat), + GFP_KERNEL); + if (!plat_dat) + return -ENOMEM; + + ret = dwmac_generic_acpi_probe_config(pdev, plat_dat); + if (ret) + return ret; } else { plat_dat = dev_get_platdata(&pdev->dev); if (!plat_dat) { dev_err(&pdev->dev, "no platform data provided\n"); @@ -52,6 +158,14 @@ static const struct of_device_id dwmac_generic_match[] = { }; MODULE_DEVICE_TABLE(of, dwmac_generic_match); +static const struct acpi_device_id dwmac_generic_acpi_match[] = { + { "FTGM0001", 0 }, + { "PHYT0004", 0 }, + { } +}; +MODULE_DEVICE_TABLE(acpi, dwmac_generic_acpi_match); + static struct platform_driver dwmac_generic_driver = { .probe = dwmac_generic_probe, .driver = { .name = STMMAC_RESOURCE_NAME, .pm = &stmmac_pltfr_pm_ops, .of_match_table = dwmac_generic_match, + .acpi_match_table = dwmac_generic_acpi_match, }, }; module_platform_driver(dwmac_generic_driver); -- 2.43.0