mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-12-28 07:35:20 +07:00
eb38401c77
Some HW specific setups, like sun8i, do not populate all the necessary
callbacks, which is what HWIF helpers were expecting.
Fix this by always trying to get the generic helpers and populate them
if they were not previously populated by HW specific setup.
Signed-off-by: Jose Abreu <joabreu@synopsys.com>
Fixes: 5f0456b431
("net: stmmac: Implement logic to automatically
select HW Interface")
Reported-by: Corentin Labbe <clabbe.montjoie@gmail.com>
Tested-by: Corentin Labbe <clabbe.montjoie@gmail.com>
Cc: Corentin Labbe <clabbe.montjoie@gmail.com>
Cc: David S. Miller <davem@davemloft.net>
Cc: Joao Pinto <jpinto@synopsys.com>
Cc: Giuseppe Cavallaro <peppe.cavallaro@st.com>
Cc: Alexandre Torgue <alexandre.torgue@st.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
269 lines
6.0 KiB
C
269 lines
6.0 KiB
C
// SPDX-License-Identifier: (GPL-2.0 OR MIT)
|
|
/*
|
|
* Copyright (c) 2018 Synopsys, Inc. and/or its affiliates.
|
|
* stmmac HW Interface Handling
|
|
*/
|
|
|
|
#include "common.h"
|
|
#include "stmmac.h"
|
|
#include "stmmac_ptp.h"
|
|
|
|
static u32 stmmac_get_id(struct stmmac_priv *priv, u32 id_reg)
|
|
{
|
|
u32 reg = readl(priv->ioaddr + id_reg);
|
|
|
|
if (!reg) {
|
|
dev_info(priv->device, "Version ID not available\n");
|
|
return 0x0;
|
|
}
|
|
|
|
dev_info(priv->device, "User ID: 0x%x, Synopsys ID: 0x%x\n",
|
|
(unsigned int)(reg & GENMASK(15, 8)) >> 8,
|
|
(unsigned int)(reg & GENMASK(7, 0)));
|
|
return reg & GENMASK(7, 0);
|
|
}
|
|
|
|
static void stmmac_dwmac_mode_quirk(struct stmmac_priv *priv)
|
|
{
|
|
struct mac_device_info *mac = priv->hw;
|
|
|
|
if (priv->chain_mode) {
|
|
dev_info(priv->device, "Chain mode enabled\n");
|
|
priv->mode = STMMAC_CHAIN_MODE;
|
|
mac->mode = &chain_mode_ops;
|
|
} else {
|
|
dev_info(priv->device, "Ring mode enabled\n");
|
|
priv->mode = STMMAC_RING_MODE;
|
|
mac->mode = &ring_mode_ops;
|
|
}
|
|
}
|
|
|
|
static int stmmac_dwmac1_quirks(struct stmmac_priv *priv)
|
|
{
|
|
struct mac_device_info *mac = priv->hw;
|
|
|
|
if (priv->plat->enh_desc) {
|
|
dev_info(priv->device, "Enhanced/Alternate descriptors\n");
|
|
|
|
/* GMAC older than 3.50 has no extended descriptors */
|
|
if (priv->synopsys_id >= DWMAC_CORE_3_50) {
|
|
dev_info(priv->device, "Enabled extended descriptors\n");
|
|
priv->extend_desc = 1;
|
|
} else {
|
|
dev_warn(priv->device, "Extended descriptors not supported\n");
|
|
}
|
|
|
|
mac->desc = &enh_desc_ops;
|
|
} else {
|
|
dev_info(priv->device, "Normal descriptors\n");
|
|
mac->desc = &ndesc_ops;
|
|
}
|
|
|
|
stmmac_dwmac_mode_quirk(priv);
|
|
return 0;
|
|
}
|
|
|
|
static int stmmac_dwmac4_quirks(struct stmmac_priv *priv)
|
|
{
|
|
stmmac_dwmac_mode_quirk(priv);
|
|
return 0;
|
|
}
|
|
|
|
static const struct stmmac_hwif_entry {
|
|
bool gmac;
|
|
bool gmac4;
|
|
u32 min_id;
|
|
const struct stmmac_regs_off regs;
|
|
const void *desc;
|
|
const void *dma;
|
|
const void *mac;
|
|
const void *hwtimestamp;
|
|
const void *mode;
|
|
const void *tc;
|
|
int (*setup)(struct stmmac_priv *priv);
|
|
int (*quirks)(struct stmmac_priv *priv);
|
|
} stmmac_hw[] = {
|
|
/* NOTE: New HW versions shall go to the end of this table */
|
|
{
|
|
.gmac = false,
|
|
.gmac4 = false,
|
|
.min_id = 0,
|
|
.regs = {
|
|
.ptp_off = PTP_GMAC3_X_OFFSET,
|
|
.mmc_off = MMC_GMAC3_X_OFFSET,
|
|
},
|
|
.desc = NULL,
|
|
.dma = &dwmac100_dma_ops,
|
|
.mac = &dwmac100_ops,
|
|
.hwtimestamp = &stmmac_ptp,
|
|
.mode = NULL,
|
|
.tc = NULL,
|
|
.setup = dwmac100_setup,
|
|
.quirks = stmmac_dwmac1_quirks,
|
|
}, {
|
|
.gmac = true,
|
|
.gmac4 = false,
|
|
.min_id = 0,
|
|
.regs = {
|
|
.ptp_off = PTP_GMAC3_X_OFFSET,
|
|
.mmc_off = MMC_GMAC3_X_OFFSET,
|
|
},
|
|
.desc = NULL,
|
|
.dma = &dwmac1000_dma_ops,
|
|
.mac = &dwmac1000_ops,
|
|
.hwtimestamp = &stmmac_ptp,
|
|
.mode = NULL,
|
|
.tc = NULL,
|
|
.setup = dwmac1000_setup,
|
|
.quirks = stmmac_dwmac1_quirks,
|
|
}, {
|
|
.gmac = false,
|
|
.gmac4 = true,
|
|
.min_id = 0,
|
|
.regs = {
|
|
.ptp_off = PTP_GMAC4_OFFSET,
|
|
.mmc_off = MMC_GMAC4_OFFSET,
|
|
},
|
|
.desc = &dwmac4_desc_ops,
|
|
.dma = &dwmac4_dma_ops,
|
|
.mac = &dwmac4_ops,
|
|
.hwtimestamp = &stmmac_ptp,
|
|
.mode = NULL,
|
|
.tc = NULL,
|
|
.setup = dwmac4_setup,
|
|
.quirks = stmmac_dwmac4_quirks,
|
|
}, {
|
|
.gmac = false,
|
|
.gmac4 = true,
|
|
.min_id = DWMAC_CORE_4_00,
|
|
.regs = {
|
|
.ptp_off = PTP_GMAC4_OFFSET,
|
|
.mmc_off = MMC_GMAC4_OFFSET,
|
|
},
|
|
.desc = &dwmac4_desc_ops,
|
|
.dma = &dwmac4_dma_ops,
|
|
.mac = &dwmac410_ops,
|
|
.hwtimestamp = &stmmac_ptp,
|
|
.mode = &dwmac4_ring_mode_ops,
|
|
.tc = NULL,
|
|
.setup = dwmac4_setup,
|
|
.quirks = NULL,
|
|
}, {
|
|
.gmac = false,
|
|
.gmac4 = true,
|
|
.min_id = DWMAC_CORE_4_10,
|
|
.regs = {
|
|
.ptp_off = PTP_GMAC4_OFFSET,
|
|
.mmc_off = MMC_GMAC4_OFFSET,
|
|
},
|
|
.desc = &dwmac4_desc_ops,
|
|
.dma = &dwmac410_dma_ops,
|
|
.mac = &dwmac410_ops,
|
|
.hwtimestamp = &stmmac_ptp,
|
|
.mode = &dwmac4_ring_mode_ops,
|
|
.tc = NULL,
|
|
.setup = dwmac4_setup,
|
|
.quirks = NULL,
|
|
}, {
|
|
.gmac = false,
|
|
.gmac4 = true,
|
|
.min_id = DWMAC_CORE_5_10,
|
|
.regs = {
|
|
.ptp_off = PTP_GMAC4_OFFSET,
|
|
.mmc_off = MMC_GMAC4_OFFSET,
|
|
},
|
|
.desc = &dwmac4_desc_ops,
|
|
.dma = &dwmac410_dma_ops,
|
|
.mac = &dwmac510_ops,
|
|
.hwtimestamp = &stmmac_ptp,
|
|
.mode = &dwmac4_ring_mode_ops,
|
|
.tc = &dwmac510_tc_ops,
|
|
.setup = dwmac4_setup,
|
|
.quirks = NULL,
|
|
}
|
|
};
|
|
|
|
int stmmac_hwif_init(struct stmmac_priv *priv)
|
|
{
|
|
bool needs_gmac4 = priv->plat->has_gmac4;
|
|
bool needs_gmac = priv->plat->has_gmac;
|
|
const struct stmmac_hwif_entry *entry;
|
|
struct mac_device_info *mac;
|
|
bool needs_setup = true;
|
|
int i, ret;
|
|
u32 id;
|
|
|
|
if (needs_gmac) {
|
|
id = stmmac_get_id(priv, GMAC_VERSION);
|
|
} else if (needs_gmac4) {
|
|
id = stmmac_get_id(priv, GMAC4_VERSION);
|
|
} else {
|
|
id = 0;
|
|
}
|
|
|
|
/* Save ID for later use */
|
|
priv->synopsys_id = id;
|
|
|
|
/* Lets assume some safe values first */
|
|
priv->ptpaddr = priv->ioaddr +
|
|
(needs_gmac4 ? PTP_GMAC4_OFFSET : PTP_GMAC3_X_OFFSET);
|
|
priv->mmcaddr = priv->ioaddr +
|
|
(needs_gmac4 ? MMC_GMAC4_OFFSET : MMC_GMAC3_X_OFFSET);
|
|
|
|
/* Check for HW specific setup first */
|
|
if (priv->plat->setup) {
|
|
mac = priv->plat->setup(priv);
|
|
needs_setup = false;
|
|
} else {
|
|
mac = devm_kzalloc(priv->device, sizeof(*mac), GFP_KERNEL);
|
|
}
|
|
|
|
if (!mac)
|
|
return -ENOMEM;
|
|
|
|
/* Fallback to generic HW */
|
|
for (i = ARRAY_SIZE(stmmac_hw) - 1; i >= 0; i--) {
|
|
entry = &stmmac_hw[i];
|
|
|
|
if (needs_gmac ^ entry->gmac)
|
|
continue;
|
|
if (needs_gmac4 ^ entry->gmac4)
|
|
continue;
|
|
/* Use synopsys_id var because some setups can override this */
|
|
if (priv->synopsys_id < entry->min_id)
|
|
continue;
|
|
|
|
/* Only use generic HW helpers if needed */
|
|
mac->desc = mac->desc ? : entry->desc;
|
|
mac->dma = mac->dma ? : entry->dma;
|
|
mac->mac = mac->mac ? : entry->mac;
|
|
mac->ptp = mac->ptp ? : entry->hwtimestamp;
|
|
mac->mode = mac->mode ? : entry->mode;
|
|
mac->tc = mac->tc ? : entry->tc;
|
|
|
|
priv->hw = mac;
|
|
priv->ptpaddr = priv->ioaddr + entry->regs.ptp_off;
|
|
priv->mmcaddr = priv->ioaddr + entry->regs.mmc_off;
|
|
|
|
/* Entry found */
|
|
if (needs_setup) {
|
|
ret = entry->setup(priv);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
|
|
/* Run quirks, if needed */
|
|
if (entry->quirks) {
|
|
ret = entry->quirks(priv);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
dev_err(priv->device, "Failed to find HW IF (id=0x%x, gmac=%d/%d)\n",
|
|
id, needs_gmac, needs_gmac4);
|
|
return -EINVAL;
|
|
}
|