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 265f0456b4SJose Abreu static void stmmac_dwmac_mode_quirk(struct stmmac_priv *priv) 275f0456b4SJose Abreu { 285f0456b4SJose Abreu struct mac_device_info *mac = priv->hw; 295f0456b4SJose Abreu 305f0456b4SJose Abreu if (priv->chain_mode) { 315f0456b4SJose Abreu dev_info(priv->device, "Chain mode enabled\n"); 325f0456b4SJose Abreu priv->mode = STMMAC_CHAIN_MODE; 335f0456b4SJose Abreu mac->mode = &chain_mode_ops; 345f0456b4SJose Abreu } else { 355f0456b4SJose Abreu dev_info(priv->device, "Ring mode enabled\n"); 365f0456b4SJose Abreu priv->mode = STMMAC_RING_MODE; 375f0456b4SJose Abreu mac->mode = &ring_mode_ops; 385f0456b4SJose Abreu } 395f0456b4SJose Abreu } 405f0456b4SJose Abreu 415f0456b4SJose Abreu static int stmmac_dwmac1_quirks(struct stmmac_priv *priv) 425f0456b4SJose Abreu { 435f0456b4SJose Abreu struct mac_device_info *mac = priv->hw; 445f0456b4SJose Abreu 455f0456b4SJose Abreu if (priv->plat->enh_desc) { 465f0456b4SJose Abreu dev_info(priv->device, "Enhanced/Alternate descriptors\n"); 475f0456b4SJose Abreu 485f0456b4SJose Abreu /* GMAC older than 3.50 has no extended descriptors */ 495f0456b4SJose Abreu if (priv->synopsys_id >= DWMAC_CORE_3_50) { 505f0456b4SJose Abreu dev_info(priv->device, "Enabled extended descriptors\n"); 515f0456b4SJose Abreu priv->extend_desc = 1; 525f0456b4SJose Abreu } else { 535f0456b4SJose Abreu dev_warn(priv->device, "Extended descriptors not supported\n"); 545f0456b4SJose Abreu } 555f0456b4SJose Abreu 565f0456b4SJose Abreu mac->desc = &enh_desc_ops; 575f0456b4SJose Abreu } else { 585f0456b4SJose Abreu dev_info(priv->device, "Normal descriptors\n"); 595f0456b4SJose Abreu mac->desc = &ndesc_ops; 605f0456b4SJose Abreu } 615f0456b4SJose Abreu 625f0456b4SJose Abreu stmmac_dwmac_mode_quirk(priv); 635f0456b4SJose Abreu return 0; 645f0456b4SJose Abreu } 655f0456b4SJose Abreu 665f0456b4SJose Abreu static int stmmac_dwmac4_quirks(struct stmmac_priv *priv) 675f0456b4SJose Abreu { 685f0456b4SJose Abreu stmmac_dwmac_mode_quirk(priv); 695f0456b4SJose Abreu return 0; 705f0456b4SJose Abreu } 715f0456b4SJose Abreu 725f0456b4SJose Abreu static const struct stmmac_hwif_entry { 735f0456b4SJose Abreu bool gmac; 745f0456b4SJose Abreu bool gmac4; 7548ae5554SJose Abreu bool xgmac; 765f0456b4SJose Abreu u32 min_id; 77758d5c73SJose Abreu const struct stmmac_regs_off regs; 785f0456b4SJose Abreu const void *desc; 795f0456b4SJose Abreu const void *dma; 805f0456b4SJose Abreu const void *mac; 815f0456b4SJose Abreu const void *hwtimestamp; 825f0456b4SJose Abreu const void *mode; 834dbbe8ddSJose Abreu const void *tc; 845f0456b4SJose Abreu int (*setup)(struct stmmac_priv *priv); 855f0456b4SJose Abreu int (*quirks)(struct stmmac_priv *priv); 865f0456b4SJose Abreu } stmmac_hw[] = { 875f0456b4SJose Abreu /* NOTE: New HW versions shall go to the end of this table */ 885f0456b4SJose Abreu { 895f0456b4SJose Abreu .gmac = false, 905f0456b4SJose Abreu .gmac4 = false, 9148ae5554SJose Abreu .xgmac = false, 925f0456b4SJose Abreu .min_id = 0, 93758d5c73SJose Abreu .regs = { 94758d5c73SJose Abreu .ptp_off = PTP_GMAC3_X_OFFSET, 95758d5c73SJose Abreu .mmc_off = MMC_GMAC3_X_OFFSET, 96758d5c73SJose Abreu }, 975f0456b4SJose Abreu .desc = NULL, 985f0456b4SJose Abreu .dma = &dwmac100_dma_ops, 995f0456b4SJose Abreu .mac = &dwmac100_ops, 1005f0456b4SJose Abreu .hwtimestamp = &stmmac_ptp, 1015f0456b4SJose Abreu .mode = NULL, 1024dbbe8ddSJose Abreu .tc = NULL, 1035f0456b4SJose Abreu .setup = dwmac100_setup, 1045f0456b4SJose Abreu .quirks = stmmac_dwmac1_quirks, 1055f0456b4SJose Abreu }, { 1065f0456b4SJose Abreu .gmac = true, 1075f0456b4SJose Abreu .gmac4 = false, 10848ae5554SJose Abreu .xgmac = false, 1095f0456b4SJose Abreu .min_id = 0, 110758d5c73SJose Abreu .regs = { 111758d5c73SJose Abreu .ptp_off = PTP_GMAC3_X_OFFSET, 112758d5c73SJose Abreu .mmc_off = MMC_GMAC3_X_OFFSET, 113758d5c73SJose Abreu }, 1145f0456b4SJose Abreu .desc = NULL, 1155f0456b4SJose Abreu .dma = &dwmac1000_dma_ops, 1165f0456b4SJose Abreu .mac = &dwmac1000_ops, 1175f0456b4SJose Abreu .hwtimestamp = &stmmac_ptp, 1185f0456b4SJose Abreu .mode = NULL, 1194dbbe8ddSJose Abreu .tc = NULL, 1205f0456b4SJose Abreu .setup = dwmac1000_setup, 1215f0456b4SJose Abreu .quirks = stmmac_dwmac1_quirks, 1225f0456b4SJose Abreu }, { 1235f0456b4SJose Abreu .gmac = false, 1245f0456b4SJose Abreu .gmac4 = true, 12548ae5554SJose Abreu .xgmac = false, 1265f0456b4SJose Abreu .min_id = 0, 127758d5c73SJose Abreu .regs = { 128758d5c73SJose Abreu .ptp_off = PTP_GMAC4_OFFSET, 129758d5c73SJose Abreu .mmc_off = MMC_GMAC4_OFFSET, 130758d5c73SJose Abreu }, 1315f0456b4SJose Abreu .desc = &dwmac4_desc_ops, 1325f0456b4SJose Abreu .dma = &dwmac4_dma_ops, 1335f0456b4SJose Abreu .mac = &dwmac4_ops, 1345f0456b4SJose Abreu .hwtimestamp = &stmmac_ptp, 1355f0456b4SJose Abreu .mode = NULL, 1364dbbe8ddSJose Abreu .tc = NULL, 1375f0456b4SJose Abreu .setup = dwmac4_setup, 1385f0456b4SJose Abreu .quirks = stmmac_dwmac4_quirks, 1395f0456b4SJose Abreu }, { 1405f0456b4SJose Abreu .gmac = false, 1415f0456b4SJose Abreu .gmac4 = true, 14248ae5554SJose Abreu .xgmac = false, 1435f0456b4SJose Abreu .min_id = DWMAC_CORE_4_00, 144758d5c73SJose Abreu .regs = { 145758d5c73SJose Abreu .ptp_off = PTP_GMAC4_OFFSET, 146758d5c73SJose Abreu .mmc_off = MMC_GMAC4_OFFSET, 147758d5c73SJose Abreu }, 1485f0456b4SJose Abreu .desc = &dwmac4_desc_ops, 1495f0456b4SJose Abreu .dma = &dwmac4_dma_ops, 1505f0456b4SJose Abreu .mac = &dwmac410_ops, 1515f0456b4SJose Abreu .hwtimestamp = &stmmac_ptp, 1525f0456b4SJose Abreu .mode = &dwmac4_ring_mode_ops, 1534dbbe8ddSJose Abreu .tc = NULL, 1545f0456b4SJose Abreu .setup = dwmac4_setup, 1555f0456b4SJose Abreu .quirks = NULL, 1565f0456b4SJose Abreu }, { 1575f0456b4SJose Abreu .gmac = false, 1585f0456b4SJose Abreu .gmac4 = true, 15948ae5554SJose Abreu .xgmac = false, 1605f0456b4SJose Abreu .min_id = DWMAC_CORE_4_10, 161758d5c73SJose Abreu .regs = { 162758d5c73SJose Abreu .ptp_off = PTP_GMAC4_OFFSET, 163758d5c73SJose Abreu .mmc_off = MMC_GMAC4_OFFSET, 164758d5c73SJose Abreu }, 1655f0456b4SJose Abreu .desc = &dwmac4_desc_ops, 1665f0456b4SJose Abreu .dma = &dwmac410_dma_ops, 1675f0456b4SJose Abreu .mac = &dwmac410_ops, 1685f0456b4SJose Abreu .hwtimestamp = &stmmac_ptp, 1695f0456b4SJose Abreu .mode = &dwmac4_ring_mode_ops, 1704dbbe8ddSJose Abreu .tc = NULL, 1715f0456b4SJose Abreu .setup = dwmac4_setup, 1725f0456b4SJose Abreu .quirks = NULL, 1735f0456b4SJose Abreu }, { 1745f0456b4SJose Abreu .gmac = false, 1755f0456b4SJose Abreu .gmac4 = true, 17648ae5554SJose Abreu .xgmac = false, 1775f0456b4SJose Abreu .min_id = DWMAC_CORE_5_10, 178758d5c73SJose Abreu .regs = { 179758d5c73SJose Abreu .ptp_off = PTP_GMAC4_OFFSET, 180758d5c73SJose Abreu .mmc_off = MMC_GMAC4_OFFSET, 181758d5c73SJose Abreu }, 1825f0456b4SJose Abreu .desc = &dwmac4_desc_ops, 1835f0456b4SJose Abreu .dma = &dwmac410_dma_ops, 1845f0456b4SJose Abreu .mac = &dwmac510_ops, 1855f0456b4SJose Abreu .hwtimestamp = &stmmac_ptp, 1865f0456b4SJose Abreu .mode = &dwmac4_ring_mode_ops, 1874dbbe8ddSJose Abreu .tc = &dwmac510_tc_ops, 1885f0456b4SJose Abreu .setup = dwmac4_setup, 1895f0456b4SJose Abreu .quirks = NULL, 19048ae5554SJose Abreu }, { 19148ae5554SJose Abreu .gmac = false, 19248ae5554SJose Abreu .gmac4 = false, 19348ae5554SJose Abreu .xgmac = true, 19448ae5554SJose Abreu .min_id = DWXGMAC_CORE_2_10, 19548ae5554SJose Abreu .regs = { 19648ae5554SJose Abreu .ptp_off = 0, 19748ae5554SJose Abreu .mmc_off = 0, 19848ae5554SJose Abreu }, 19948ae5554SJose Abreu .desc = NULL, 200*d6ddfacdSJose Abreu .dma = &dwxgmac210_dma_ops, 2012142754fSJose Abreu .mac = &dwxgmac210_ops, 20248ae5554SJose Abreu .hwtimestamp = NULL, 20348ae5554SJose Abreu .mode = NULL, 20448ae5554SJose Abreu .tc = NULL, 2052142754fSJose Abreu .setup = dwxgmac2_setup, 20648ae5554SJose Abreu .quirks = NULL, 20748ae5554SJose Abreu }, 2085f0456b4SJose Abreu }; 2095f0456b4SJose Abreu 2105f0456b4SJose Abreu int stmmac_hwif_init(struct stmmac_priv *priv) 2115f0456b4SJose Abreu { 21248ae5554SJose Abreu bool needs_xgmac = priv->plat->has_xgmac; 2135f0456b4SJose Abreu bool needs_gmac4 = priv->plat->has_gmac4; 2145f0456b4SJose Abreu bool needs_gmac = priv->plat->has_gmac; 2155f0456b4SJose Abreu const struct stmmac_hwif_entry *entry; 2165f0456b4SJose Abreu struct mac_device_info *mac; 217eb38401cSJose Abreu bool needs_setup = true; 2185f0456b4SJose Abreu int i, ret; 2195f0456b4SJose Abreu u32 id; 2205f0456b4SJose Abreu 2215f0456b4SJose Abreu if (needs_gmac) { 2225f0456b4SJose Abreu id = stmmac_get_id(priv, GMAC_VERSION); 22348ae5554SJose Abreu } else if (needs_gmac4 || needs_xgmac) { 2245f0456b4SJose Abreu id = stmmac_get_id(priv, GMAC4_VERSION); 225eb38401cSJose Abreu } else { 226eb38401cSJose Abreu id = 0; 2275f0456b4SJose Abreu } 2285f0456b4SJose Abreu 2295f0456b4SJose Abreu /* Save ID for later use */ 2305f0456b4SJose Abreu priv->synopsys_id = id; 2315f0456b4SJose Abreu 232758d5c73SJose Abreu /* Lets assume some safe values first */ 233758d5c73SJose Abreu priv->ptpaddr = priv->ioaddr + 234758d5c73SJose Abreu (needs_gmac4 ? PTP_GMAC4_OFFSET : PTP_GMAC3_X_OFFSET); 235758d5c73SJose Abreu priv->mmcaddr = priv->ioaddr + 236758d5c73SJose Abreu (needs_gmac4 ? MMC_GMAC4_OFFSET : MMC_GMAC3_X_OFFSET); 237758d5c73SJose Abreu 2385f0456b4SJose Abreu /* Check for HW specific setup first */ 2395f0456b4SJose Abreu if (priv->plat->setup) { 240eb38401cSJose Abreu mac = priv->plat->setup(priv); 241eb38401cSJose Abreu needs_setup = false; 242eb38401cSJose Abreu } else { 243eb38401cSJose Abreu mac = devm_kzalloc(priv->device, sizeof(*mac), GFP_KERNEL); 2445f0456b4SJose Abreu } 2455f0456b4SJose Abreu 2465f0456b4SJose Abreu if (!mac) 2475f0456b4SJose Abreu return -ENOMEM; 2485f0456b4SJose Abreu 2495f0456b4SJose Abreu /* Fallback to generic HW */ 2505f0456b4SJose Abreu for (i = ARRAY_SIZE(stmmac_hw) - 1; i >= 0; i--) { 2515f0456b4SJose Abreu entry = &stmmac_hw[i]; 2525f0456b4SJose Abreu 2535f0456b4SJose Abreu if (needs_gmac ^ entry->gmac) 2545f0456b4SJose Abreu continue; 2555f0456b4SJose Abreu if (needs_gmac4 ^ entry->gmac4) 2565f0456b4SJose Abreu continue; 25748ae5554SJose Abreu if (needs_xgmac ^ entry->xgmac) 25848ae5554SJose Abreu continue; 259eb38401cSJose Abreu /* Use synopsys_id var because some setups can override this */ 260eb38401cSJose Abreu if (priv->synopsys_id < entry->min_id) 2615f0456b4SJose Abreu continue; 2625f0456b4SJose Abreu 263eb38401cSJose Abreu /* Only use generic HW helpers if needed */ 264eb38401cSJose Abreu mac->desc = mac->desc ? : entry->desc; 265eb38401cSJose Abreu mac->dma = mac->dma ? : entry->dma; 266eb38401cSJose Abreu mac->mac = mac->mac ? : entry->mac; 267eb38401cSJose Abreu mac->ptp = mac->ptp ? : entry->hwtimestamp; 268eb38401cSJose Abreu mac->mode = mac->mode ? : entry->mode; 269eb38401cSJose Abreu mac->tc = mac->tc ? : entry->tc; 2705f0456b4SJose Abreu 2715f0456b4SJose Abreu priv->hw = mac; 272758d5c73SJose Abreu priv->ptpaddr = priv->ioaddr + entry->regs.ptp_off; 273758d5c73SJose Abreu priv->mmcaddr = priv->ioaddr + entry->regs.mmc_off; 2745f0456b4SJose Abreu 2755f0456b4SJose Abreu /* Entry found */ 276eb38401cSJose Abreu if (needs_setup) { 2775f0456b4SJose Abreu ret = entry->setup(priv); 2785f0456b4SJose Abreu if (ret) 2795f0456b4SJose Abreu return ret; 280eb38401cSJose Abreu } 2815f0456b4SJose Abreu 2827cfde0afSJose Abreu /* Save quirks, if needed for posterior use */ 2837cfde0afSJose Abreu priv->hwif_quirks = entry->quirks; 2845f0456b4SJose Abreu return 0; 2855f0456b4SJose Abreu } 2865f0456b4SJose Abreu 2875f0456b4SJose Abreu dev_err(priv->device, "Failed to find HW IF (id=0x%x, gmac=%d/%d)\n", 2885f0456b4SJose Abreu id, needs_gmac, needs_gmac4); 2895f0456b4SJose Abreu return -EINVAL; 2905f0456b4SJose Abreu } 291