15f0456b4SJose Abreu // SPDX-License-Identifier: (GPL-2.0 OR MIT) 25f0456b4SJose Abreu /* 35f0456b4SJose Abreu * Copyright (c) 2018 Synopsys, Inc. and/or its affiliates. 45f0456b4SJose Abreu * stmmac HW Interface Handling 55f0456b4SJose Abreu */ 65f0456b4SJose Abreu 75f0456b4SJose Abreu #include "common.h" 85f0456b4SJose Abreu #include "stmmac.h" 9758d5c73SJose Abreu #include "stmmac_ptp.h" 105f0456b4SJose Abreu 115f0456b4SJose Abreu static u32 stmmac_get_id(struct stmmac_priv *priv, u32 id_reg) 125f0456b4SJose Abreu { 135f0456b4SJose Abreu u32 reg = readl(priv->ioaddr + id_reg); 145f0456b4SJose Abreu 155f0456b4SJose Abreu if (!reg) { 165f0456b4SJose Abreu dev_info(priv->device, "Version ID not available\n"); 175f0456b4SJose Abreu return 0x0; 185f0456b4SJose Abreu } 195f0456b4SJose Abreu 205f0456b4SJose Abreu dev_info(priv->device, "User ID: 0x%x, Synopsys ID: 0x%x\n", 215f0456b4SJose Abreu (unsigned int)(reg & GENMASK(15, 8)) >> 8, 225f0456b4SJose Abreu (unsigned int)(reg & GENMASK(7, 0))); 235f0456b4SJose Abreu return reg & GENMASK(7, 0); 245f0456b4SJose Abreu } 255f0456b4SJose Abreu 264a4ccde0SJose Abreu static u32 stmmac_get_dev_id(struct stmmac_priv *priv, u32 id_reg) 274a4ccde0SJose Abreu { 284a4ccde0SJose Abreu u32 reg = readl(priv->ioaddr + id_reg); 294a4ccde0SJose Abreu 304a4ccde0SJose Abreu if (!reg) { 314a4ccde0SJose Abreu dev_info(priv->device, "Version ID not available\n"); 324a4ccde0SJose Abreu return 0x0; 334a4ccde0SJose Abreu } 344a4ccde0SJose Abreu 354a4ccde0SJose Abreu return (reg & GENMASK(15, 8)) >> 8; 364a4ccde0SJose Abreu } 374a4ccde0SJose Abreu 385f0456b4SJose Abreu static void stmmac_dwmac_mode_quirk(struct stmmac_priv *priv) 395f0456b4SJose Abreu { 405f0456b4SJose Abreu struct mac_device_info *mac = priv->hw; 415f0456b4SJose Abreu 425f0456b4SJose Abreu if (priv->chain_mode) { 435f0456b4SJose Abreu dev_info(priv->device, "Chain mode enabled\n"); 445f0456b4SJose Abreu priv->mode = STMMAC_CHAIN_MODE; 455f0456b4SJose Abreu mac->mode = &chain_mode_ops; 465f0456b4SJose Abreu } else { 475f0456b4SJose Abreu dev_info(priv->device, "Ring mode enabled\n"); 485f0456b4SJose Abreu priv->mode = STMMAC_RING_MODE; 495f0456b4SJose Abreu mac->mode = &ring_mode_ops; 505f0456b4SJose Abreu } 515f0456b4SJose Abreu } 525f0456b4SJose Abreu 535f0456b4SJose Abreu static int stmmac_dwmac1_quirks(struct stmmac_priv *priv) 545f0456b4SJose Abreu { 555f0456b4SJose Abreu struct mac_device_info *mac = priv->hw; 565f0456b4SJose Abreu 575f0456b4SJose Abreu if (priv->plat->enh_desc) { 585f0456b4SJose Abreu dev_info(priv->device, "Enhanced/Alternate descriptors\n"); 595f0456b4SJose Abreu 605f0456b4SJose Abreu /* GMAC older than 3.50 has no extended descriptors */ 615f0456b4SJose Abreu if (priv->synopsys_id >= DWMAC_CORE_3_50) { 625f0456b4SJose Abreu dev_info(priv->device, "Enabled extended descriptors\n"); 635f0456b4SJose Abreu priv->extend_desc = 1; 645f0456b4SJose Abreu } else { 655f0456b4SJose Abreu dev_warn(priv->device, "Extended descriptors not supported\n"); 665f0456b4SJose Abreu } 675f0456b4SJose Abreu 685f0456b4SJose Abreu mac->desc = &enh_desc_ops; 695f0456b4SJose Abreu } else { 705f0456b4SJose Abreu dev_info(priv->device, "Normal descriptors\n"); 715f0456b4SJose Abreu mac->desc = &ndesc_ops; 725f0456b4SJose Abreu } 735f0456b4SJose Abreu 745f0456b4SJose Abreu stmmac_dwmac_mode_quirk(priv); 755f0456b4SJose Abreu return 0; 765f0456b4SJose Abreu } 775f0456b4SJose Abreu 785f0456b4SJose Abreu static int stmmac_dwmac4_quirks(struct stmmac_priv *priv) 795f0456b4SJose Abreu { 805f0456b4SJose Abreu stmmac_dwmac_mode_quirk(priv); 815f0456b4SJose Abreu return 0; 825f0456b4SJose Abreu } 835f0456b4SJose Abreu 844a4ccde0SJose Abreu static int stmmac_dwxlgmac_quirks(struct stmmac_priv *priv) 854a4ccde0SJose Abreu { 864a4ccde0SJose Abreu priv->hw->xlgmac = true; 874a4ccde0SJose Abreu return 0; 884a4ccde0SJose Abreu } 894a4ccde0SJose Abreu 90*10739ea3SShenwei Wang int stmmac_reset(struct stmmac_priv *priv, void __iomem *ioaddr) 91*10739ea3SShenwei Wang { 92*10739ea3SShenwei Wang struct plat_stmmacenet_data *plat = priv ? priv->plat : NULL; 93*10739ea3SShenwei Wang 94*10739ea3SShenwei Wang if (!priv) 95*10739ea3SShenwei Wang return -EINVAL; 96*10739ea3SShenwei Wang 97*10739ea3SShenwei Wang if (plat && plat->fix_soc_reset) 98*10739ea3SShenwei Wang return plat->fix_soc_reset(plat, ioaddr); 99*10739ea3SShenwei Wang 100*10739ea3SShenwei Wang return stmmac_do_callback(priv, dma, reset, ioaddr); 101*10739ea3SShenwei Wang } 102*10739ea3SShenwei Wang 1035f0456b4SJose Abreu static const struct stmmac_hwif_entry { 1045f0456b4SJose Abreu bool gmac; 1055f0456b4SJose Abreu bool gmac4; 10648ae5554SJose Abreu bool xgmac; 1075f0456b4SJose Abreu u32 min_id; 1084a4ccde0SJose Abreu u32 dev_id; 109758d5c73SJose Abreu const struct stmmac_regs_off regs; 1105f0456b4SJose Abreu const void *desc; 1115f0456b4SJose Abreu const void *dma; 1125f0456b4SJose Abreu const void *mac; 1135f0456b4SJose Abreu const void *hwtimestamp; 1145f0456b4SJose Abreu const void *mode; 1154dbbe8ddSJose Abreu const void *tc; 1163b1dd2c5SJose Abreu const void *mmc; 1175f0456b4SJose Abreu int (*setup)(struct stmmac_priv *priv); 1185f0456b4SJose Abreu int (*quirks)(struct stmmac_priv *priv); 1195f0456b4SJose Abreu } stmmac_hw[] = { 1205f0456b4SJose Abreu /* NOTE: New HW versions shall go to the end of this table */ 1215f0456b4SJose Abreu { 1225f0456b4SJose Abreu .gmac = false, 1235f0456b4SJose Abreu .gmac4 = false, 12448ae5554SJose Abreu .xgmac = false, 1255f0456b4SJose Abreu .min_id = 0, 126758d5c73SJose Abreu .regs = { 127758d5c73SJose Abreu .ptp_off = PTP_GMAC3_X_OFFSET, 128758d5c73SJose Abreu .mmc_off = MMC_GMAC3_X_OFFSET, 129758d5c73SJose Abreu }, 1305f0456b4SJose Abreu .desc = NULL, 1315f0456b4SJose Abreu .dma = &dwmac100_dma_ops, 1325f0456b4SJose Abreu .mac = &dwmac100_ops, 1335f0456b4SJose Abreu .hwtimestamp = &stmmac_ptp, 1345f0456b4SJose Abreu .mode = NULL, 1354dbbe8ddSJose Abreu .tc = NULL, 1363b1dd2c5SJose Abreu .mmc = &dwmac_mmc_ops, 1375f0456b4SJose Abreu .setup = dwmac100_setup, 1385f0456b4SJose Abreu .quirks = stmmac_dwmac1_quirks, 1395f0456b4SJose Abreu }, { 1405f0456b4SJose Abreu .gmac = true, 1415f0456b4SJose Abreu .gmac4 = false, 14248ae5554SJose Abreu .xgmac = false, 1435f0456b4SJose Abreu .min_id = 0, 144758d5c73SJose Abreu .regs = { 145758d5c73SJose Abreu .ptp_off = PTP_GMAC3_X_OFFSET, 146758d5c73SJose Abreu .mmc_off = MMC_GMAC3_X_OFFSET, 147758d5c73SJose Abreu }, 1485f0456b4SJose Abreu .desc = NULL, 1495f0456b4SJose Abreu .dma = &dwmac1000_dma_ops, 1505f0456b4SJose Abreu .mac = &dwmac1000_ops, 1515f0456b4SJose Abreu .hwtimestamp = &stmmac_ptp, 1525f0456b4SJose Abreu .mode = NULL, 1534dbbe8ddSJose Abreu .tc = NULL, 1543b1dd2c5SJose Abreu .mmc = &dwmac_mmc_ops, 1555f0456b4SJose Abreu .setup = dwmac1000_setup, 1565f0456b4SJose Abreu .quirks = stmmac_dwmac1_quirks, 1575f0456b4SJose Abreu }, { 1585f0456b4SJose Abreu .gmac = false, 1595f0456b4SJose Abreu .gmac4 = true, 16048ae5554SJose Abreu .xgmac = false, 1615f0456b4SJose Abreu .min_id = 0, 162758d5c73SJose Abreu .regs = { 163758d5c73SJose Abreu .ptp_off = PTP_GMAC4_OFFSET, 164758d5c73SJose Abreu .mmc_off = MMC_GMAC4_OFFSET, 165758d5c73SJose Abreu }, 1665f0456b4SJose Abreu .desc = &dwmac4_desc_ops, 1675f0456b4SJose Abreu .dma = &dwmac4_dma_ops, 1685f0456b4SJose Abreu .mac = &dwmac4_ops, 1695f0456b4SJose Abreu .hwtimestamp = &stmmac_ptp, 1705f0456b4SJose Abreu .mode = NULL, 171cc577b01SJose Abreu .tc = &dwmac510_tc_ops, 1723b1dd2c5SJose Abreu .mmc = &dwmac_mmc_ops, 1735f0456b4SJose Abreu .setup = dwmac4_setup, 1745f0456b4SJose Abreu .quirks = stmmac_dwmac4_quirks, 1755f0456b4SJose Abreu }, { 1765f0456b4SJose Abreu .gmac = false, 1775f0456b4SJose Abreu .gmac4 = true, 17848ae5554SJose Abreu .xgmac = false, 1795f0456b4SJose Abreu .min_id = DWMAC_CORE_4_00, 180758d5c73SJose Abreu .regs = { 181758d5c73SJose Abreu .ptp_off = PTP_GMAC4_OFFSET, 182758d5c73SJose Abreu .mmc_off = MMC_GMAC4_OFFSET, 183758d5c73SJose Abreu }, 1845f0456b4SJose Abreu .desc = &dwmac4_desc_ops, 1855f0456b4SJose Abreu .dma = &dwmac4_dma_ops, 1865f0456b4SJose Abreu .mac = &dwmac410_ops, 1875f0456b4SJose Abreu .hwtimestamp = &stmmac_ptp, 1885f0456b4SJose Abreu .mode = &dwmac4_ring_mode_ops, 189cc577b01SJose Abreu .tc = &dwmac510_tc_ops, 1903b1dd2c5SJose Abreu .mmc = &dwmac_mmc_ops, 1915f0456b4SJose Abreu .setup = dwmac4_setup, 1925f0456b4SJose Abreu .quirks = NULL, 1935f0456b4SJose Abreu }, { 1945f0456b4SJose Abreu .gmac = false, 1955f0456b4SJose Abreu .gmac4 = true, 19648ae5554SJose Abreu .xgmac = false, 1975f0456b4SJose Abreu .min_id = DWMAC_CORE_4_10, 198758d5c73SJose Abreu .regs = { 199758d5c73SJose Abreu .ptp_off = PTP_GMAC4_OFFSET, 200758d5c73SJose Abreu .mmc_off = MMC_GMAC4_OFFSET, 201758d5c73SJose Abreu }, 2025f0456b4SJose Abreu .desc = &dwmac4_desc_ops, 2035f0456b4SJose Abreu .dma = &dwmac410_dma_ops, 2045f0456b4SJose Abreu .mac = &dwmac410_ops, 2055f0456b4SJose Abreu .hwtimestamp = &stmmac_ptp, 2065f0456b4SJose Abreu .mode = &dwmac4_ring_mode_ops, 207cc577b01SJose Abreu .tc = &dwmac510_tc_ops, 2083b1dd2c5SJose Abreu .mmc = &dwmac_mmc_ops, 2095f0456b4SJose Abreu .setup = dwmac4_setup, 2105f0456b4SJose Abreu .quirks = NULL, 2115f0456b4SJose Abreu }, { 2125f0456b4SJose Abreu .gmac = false, 2135f0456b4SJose Abreu .gmac4 = true, 21448ae5554SJose Abreu .xgmac = false, 2155f0456b4SJose Abreu .min_id = DWMAC_CORE_5_10, 216758d5c73SJose Abreu .regs = { 217758d5c73SJose Abreu .ptp_off = PTP_GMAC4_OFFSET, 218758d5c73SJose Abreu .mmc_off = MMC_GMAC4_OFFSET, 219758d5c73SJose Abreu }, 2205f0456b4SJose Abreu .desc = &dwmac4_desc_ops, 2215f0456b4SJose Abreu .dma = &dwmac410_dma_ops, 2225f0456b4SJose Abreu .mac = &dwmac510_ops, 2235f0456b4SJose Abreu .hwtimestamp = &stmmac_ptp, 2245f0456b4SJose Abreu .mode = &dwmac4_ring_mode_ops, 2254dbbe8ddSJose Abreu .tc = &dwmac510_tc_ops, 2263b1dd2c5SJose Abreu .mmc = &dwmac_mmc_ops, 2275f0456b4SJose Abreu .setup = dwmac4_setup, 2285f0456b4SJose Abreu .quirks = NULL, 22948ae5554SJose Abreu }, { 23048ae5554SJose Abreu .gmac = false, 23148ae5554SJose Abreu .gmac4 = false, 23248ae5554SJose Abreu .xgmac = true, 23348ae5554SJose Abreu .min_id = DWXGMAC_CORE_2_10, 2344a4ccde0SJose Abreu .dev_id = DWXGMAC_ID, 23548ae5554SJose Abreu .regs = { 2364bb7aff9SJose Abreu .ptp_off = PTP_XGMAC_OFFSET, 237b6cdf09fSJose Abreu .mmc_off = MMC_XGMAC_OFFSET, 23848ae5554SJose Abreu }, 239874dfb65SJose Abreu .desc = &dwxgmac210_desc_ops, 240d6ddfacdSJose Abreu .dma = &dwxgmac210_dma_ops, 2412142754fSJose Abreu .mac = &dwxgmac210_ops, 2424bb7aff9SJose Abreu .hwtimestamp = &stmmac_ptp, 24348ae5554SJose Abreu .mode = NULL, 244ec6ea8e3SJose Abreu .tc = &dwmac510_tc_ops, 245b6cdf09fSJose Abreu .mmc = &dwxgmac_mmc_ops, 2462142754fSJose Abreu .setup = dwxgmac2_setup, 24748ae5554SJose Abreu .quirks = NULL, 2484a4ccde0SJose Abreu }, { 2494a4ccde0SJose Abreu .gmac = false, 2504a4ccde0SJose Abreu .gmac4 = false, 2514a4ccde0SJose Abreu .xgmac = true, 2524a4ccde0SJose Abreu .min_id = DWXLGMAC_CORE_2_00, 2534a4ccde0SJose Abreu .dev_id = DWXLGMAC_ID, 2544a4ccde0SJose Abreu .regs = { 2554a4ccde0SJose Abreu .ptp_off = PTP_XGMAC_OFFSET, 2564a4ccde0SJose Abreu .mmc_off = MMC_XGMAC_OFFSET, 2574a4ccde0SJose Abreu }, 2584a4ccde0SJose Abreu .desc = &dwxgmac210_desc_ops, 2594a4ccde0SJose Abreu .dma = &dwxgmac210_dma_ops, 2604a4ccde0SJose Abreu .mac = &dwxlgmac2_ops, 2614a4ccde0SJose Abreu .hwtimestamp = &stmmac_ptp, 2624a4ccde0SJose Abreu .mode = NULL, 2634a4ccde0SJose Abreu .tc = &dwmac510_tc_ops, 2644a4ccde0SJose Abreu .mmc = &dwxgmac_mmc_ops, 2654a4ccde0SJose Abreu .setup = dwxlgmac2_setup, 2664a4ccde0SJose Abreu .quirks = stmmac_dwxlgmac_quirks, 26748ae5554SJose Abreu }, 2685f0456b4SJose Abreu }; 2695f0456b4SJose Abreu 2705f0456b4SJose Abreu int stmmac_hwif_init(struct stmmac_priv *priv) 2715f0456b4SJose Abreu { 27248ae5554SJose Abreu bool needs_xgmac = priv->plat->has_xgmac; 2735f0456b4SJose Abreu bool needs_gmac4 = priv->plat->has_gmac4; 2745f0456b4SJose Abreu bool needs_gmac = priv->plat->has_gmac; 2755f0456b4SJose Abreu const struct stmmac_hwif_entry *entry; 2765f0456b4SJose Abreu struct mac_device_info *mac; 277eb38401cSJose Abreu bool needs_setup = true; 2784a4ccde0SJose Abreu u32 id, dev_id = 0; 2795f0456b4SJose Abreu int i, ret; 2805f0456b4SJose Abreu 2815f0456b4SJose Abreu if (needs_gmac) { 2825f0456b4SJose Abreu id = stmmac_get_id(priv, GMAC_VERSION); 28348ae5554SJose Abreu } else if (needs_gmac4 || needs_xgmac) { 2845f0456b4SJose Abreu id = stmmac_get_id(priv, GMAC4_VERSION); 2854a4ccde0SJose Abreu if (needs_xgmac) 2864a4ccde0SJose Abreu dev_id = stmmac_get_dev_id(priv, GMAC4_VERSION); 287eb38401cSJose Abreu } else { 288eb38401cSJose Abreu id = 0; 2895f0456b4SJose Abreu } 2905f0456b4SJose Abreu 2915f0456b4SJose Abreu /* Save ID for later use */ 2925f0456b4SJose Abreu priv->synopsys_id = id; 2935f0456b4SJose Abreu 294758d5c73SJose Abreu /* Lets assume some safe values first */ 295758d5c73SJose Abreu priv->ptpaddr = priv->ioaddr + 296758d5c73SJose Abreu (needs_gmac4 ? PTP_GMAC4_OFFSET : PTP_GMAC3_X_OFFSET); 297758d5c73SJose Abreu priv->mmcaddr = priv->ioaddr + 298758d5c73SJose Abreu (needs_gmac4 ? MMC_GMAC4_OFFSET : MMC_GMAC3_X_OFFSET); 299758d5c73SJose Abreu 3005f0456b4SJose Abreu /* Check for HW specific setup first */ 3015f0456b4SJose Abreu if (priv->plat->setup) { 302eb38401cSJose Abreu mac = priv->plat->setup(priv); 303eb38401cSJose Abreu needs_setup = false; 304eb38401cSJose Abreu } else { 305eb38401cSJose Abreu mac = devm_kzalloc(priv->device, sizeof(*mac), GFP_KERNEL); 3065f0456b4SJose Abreu } 3075f0456b4SJose Abreu 3085f0456b4SJose Abreu if (!mac) 3095f0456b4SJose Abreu return -ENOMEM; 3105f0456b4SJose Abreu 3115f0456b4SJose Abreu /* Fallback to generic HW */ 3125f0456b4SJose Abreu for (i = ARRAY_SIZE(stmmac_hw) - 1; i >= 0; i--) { 3135f0456b4SJose Abreu entry = &stmmac_hw[i]; 3145f0456b4SJose Abreu 3155f0456b4SJose Abreu if (needs_gmac ^ entry->gmac) 3165f0456b4SJose Abreu continue; 3175f0456b4SJose Abreu if (needs_gmac4 ^ entry->gmac4) 3185f0456b4SJose Abreu continue; 31948ae5554SJose Abreu if (needs_xgmac ^ entry->xgmac) 32048ae5554SJose Abreu continue; 321eb38401cSJose Abreu /* Use synopsys_id var because some setups can override this */ 322eb38401cSJose Abreu if (priv->synopsys_id < entry->min_id) 3235f0456b4SJose Abreu continue; 3244a4ccde0SJose Abreu if (needs_xgmac && (dev_id ^ entry->dev_id)) 3254a4ccde0SJose Abreu continue; 3265f0456b4SJose Abreu 327eb38401cSJose Abreu /* Only use generic HW helpers if needed */ 328eb38401cSJose Abreu mac->desc = mac->desc ? : entry->desc; 329eb38401cSJose Abreu mac->dma = mac->dma ? : entry->dma; 330eb38401cSJose Abreu mac->mac = mac->mac ? : entry->mac; 331eb38401cSJose Abreu mac->ptp = mac->ptp ? : entry->hwtimestamp; 332eb38401cSJose Abreu mac->mode = mac->mode ? : entry->mode; 333eb38401cSJose Abreu mac->tc = mac->tc ? : entry->tc; 3343b1dd2c5SJose Abreu mac->mmc = mac->mmc ? : entry->mmc; 3355f0456b4SJose Abreu 3365f0456b4SJose Abreu priv->hw = mac; 337758d5c73SJose Abreu priv->ptpaddr = priv->ioaddr + entry->regs.ptp_off; 338758d5c73SJose Abreu priv->mmcaddr = priv->ioaddr + entry->regs.mmc_off; 3395f0456b4SJose Abreu 3405f0456b4SJose Abreu /* Entry found */ 341eb38401cSJose Abreu if (needs_setup) { 3425f0456b4SJose Abreu ret = entry->setup(priv); 3435f0456b4SJose Abreu if (ret) 3445f0456b4SJose Abreu return ret; 345eb38401cSJose Abreu } 3465f0456b4SJose Abreu 3477cfde0afSJose Abreu /* Save quirks, if needed for posterior use */ 3487cfde0afSJose Abreu priv->hwif_quirks = entry->quirks; 3495f0456b4SJose Abreu return 0; 3505f0456b4SJose Abreu } 3515f0456b4SJose Abreu 3525f0456b4SJose Abreu dev_err(priv->device, "Failed to find HW IF (id=0x%x, gmac=%d/%d)\n", 3535f0456b4SJose Abreu id, needs_gmac, needs_gmac4); 3545f0456b4SJose Abreu return -EINVAL; 3555f0456b4SJose Abreu } 356