1 // SPDX-License-Identifier: (GPL-2.0 OR MIT) 2 /* 3 * Copyright (c) 2018 Synopsys, Inc. and/or its affiliates. 4 * stmmac HW Interface Handling 5 */ 6 7 #include "common.h" 8 #include "stmmac.h" 9 #include "stmmac_ptp.h" 10 11 static u32 stmmac_get_id(struct stmmac_priv *priv, u32 id_reg) 12 { 13 u32 reg = readl(priv->ioaddr + id_reg); 14 15 if (!reg) { 16 dev_info(priv->device, "Version ID not available\n"); 17 return 0x0; 18 } 19 20 dev_info(priv->device, "User ID: 0x%x, Synopsys ID: 0x%x\n", 21 (unsigned int)(reg & GENMASK(15, 8)) >> 8, 22 (unsigned int)(reg & GENMASK(7, 0))); 23 return reg & GENMASK(7, 0); 24 } 25 26 static void stmmac_dwmac_mode_quirk(struct stmmac_priv *priv) 27 { 28 struct mac_device_info *mac = priv->hw; 29 30 if (priv->chain_mode) { 31 dev_info(priv->device, "Chain mode enabled\n"); 32 priv->mode = STMMAC_CHAIN_MODE; 33 mac->mode = &chain_mode_ops; 34 } else { 35 dev_info(priv->device, "Ring mode enabled\n"); 36 priv->mode = STMMAC_RING_MODE; 37 mac->mode = &ring_mode_ops; 38 } 39 } 40 41 static int stmmac_dwmac1_quirks(struct stmmac_priv *priv) 42 { 43 struct mac_device_info *mac = priv->hw; 44 45 if (priv->plat->enh_desc) { 46 dev_info(priv->device, "Enhanced/Alternate descriptors\n"); 47 48 /* GMAC older than 3.50 has no extended descriptors */ 49 if (priv->synopsys_id >= DWMAC_CORE_3_50) { 50 dev_info(priv->device, "Enabled extended descriptors\n"); 51 priv->extend_desc = 1; 52 } else { 53 dev_warn(priv->device, "Extended descriptors not supported\n"); 54 } 55 56 mac->desc = &enh_desc_ops; 57 } else { 58 dev_info(priv->device, "Normal descriptors\n"); 59 mac->desc = &ndesc_ops; 60 } 61 62 stmmac_dwmac_mode_quirk(priv); 63 return 0; 64 } 65 66 static int stmmac_dwmac4_quirks(struct stmmac_priv *priv) 67 { 68 stmmac_dwmac_mode_quirk(priv); 69 return 0; 70 } 71 72 static const struct stmmac_hwif_entry { 73 bool gmac; 74 bool gmac4; 75 u32 min_id; 76 const struct stmmac_regs_off regs; 77 const void *desc; 78 const void *dma; 79 const void *mac; 80 const void *hwtimestamp; 81 const void *mode; 82 const void *tc; 83 int (*setup)(struct stmmac_priv *priv); 84 int (*quirks)(struct stmmac_priv *priv); 85 } stmmac_hw[] = { 86 /* NOTE: New HW versions shall go to the end of this table */ 87 { 88 .gmac = false, 89 .gmac4 = false, 90 .min_id = 0, 91 .regs = { 92 .ptp_off = PTP_GMAC3_X_OFFSET, 93 .mmc_off = MMC_GMAC3_X_OFFSET, 94 }, 95 .desc = NULL, 96 .dma = &dwmac100_dma_ops, 97 .mac = &dwmac100_ops, 98 .hwtimestamp = &stmmac_ptp, 99 .mode = NULL, 100 .tc = NULL, 101 .setup = dwmac100_setup, 102 .quirks = stmmac_dwmac1_quirks, 103 }, { 104 .gmac = true, 105 .gmac4 = false, 106 .min_id = 0, 107 .regs = { 108 .ptp_off = PTP_GMAC3_X_OFFSET, 109 .mmc_off = MMC_GMAC3_X_OFFSET, 110 }, 111 .desc = NULL, 112 .dma = &dwmac1000_dma_ops, 113 .mac = &dwmac1000_ops, 114 .hwtimestamp = &stmmac_ptp, 115 .mode = NULL, 116 .tc = NULL, 117 .setup = dwmac1000_setup, 118 .quirks = stmmac_dwmac1_quirks, 119 }, { 120 .gmac = false, 121 .gmac4 = true, 122 .min_id = 0, 123 .regs = { 124 .ptp_off = PTP_GMAC4_OFFSET, 125 .mmc_off = MMC_GMAC4_OFFSET, 126 }, 127 .desc = &dwmac4_desc_ops, 128 .dma = &dwmac4_dma_ops, 129 .mac = &dwmac4_ops, 130 .hwtimestamp = &stmmac_ptp, 131 .mode = NULL, 132 .tc = NULL, 133 .setup = dwmac4_setup, 134 .quirks = stmmac_dwmac4_quirks, 135 }, { 136 .gmac = false, 137 .gmac4 = true, 138 .min_id = DWMAC_CORE_4_00, 139 .regs = { 140 .ptp_off = PTP_GMAC4_OFFSET, 141 .mmc_off = MMC_GMAC4_OFFSET, 142 }, 143 .desc = &dwmac4_desc_ops, 144 .dma = &dwmac4_dma_ops, 145 .mac = &dwmac410_ops, 146 .hwtimestamp = &stmmac_ptp, 147 .mode = &dwmac4_ring_mode_ops, 148 .tc = NULL, 149 .setup = dwmac4_setup, 150 .quirks = NULL, 151 }, { 152 .gmac = false, 153 .gmac4 = true, 154 .min_id = DWMAC_CORE_4_10, 155 .regs = { 156 .ptp_off = PTP_GMAC4_OFFSET, 157 .mmc_off = MMC_GMAC4_OFFSET, 158 }, 159 .desc = &dwmac4_desc_ops, 160 .dma = &dwmac410_dma_ops, 161 .mac = &dwmac410_ops, 162 .hwtimestamp = &stmmac_ptp, 163 .mode = &dwmac4_ring_mode_ops, 164 .tc = NULL, 165 .setup = dwmac4_setup, 166 .quirks = NULL, 167 }, { 168 .gmac = false, 169 .gmac4 = true, 170 .min_id = DWMAC_CORE_5_10, 171 .regs = { 172 .ptp_off = PTP_GMAC4_OFFSET, 173 .mmc_off = MMC_GMAC4_OFFSET, 174 }, 175 .desc = &dwmac4_desc_ops, 176 .dma = &dwmac410_dma_ops, 177 .mac = &dwmac510_ops, 178 .hwtimestamp = &stmmac_ptp, 179 .mode = &dwmac4_ring_mode_ops, 180 .tc = &dwmac510_tc_ops, 181 .setup = dwmac4_setup, 182 .quirks = NULL, 183 } 184 }; 185 186 int stmmac_hwif_init(struct stmmac_priv *priv) 187 { 188 bool needs_gmac4 = priv->plat->has_gmac4; 189 bool needs_gmac = priv->plat->has_gmac; 190 const struct stmmac_hwif_entry *entry; 191 struct mac_device_info *mac; 192 bool needs_setup = true; 193 int i, ret; 194 u32 id; 195 196 if (needs_gmac) { 197 id = stmmac_get_id(priv, GMAC_VERSION); 198 } else if (needs_gmac4) { 199 id = stmmac_get_id(priv, GMAC4_VERSION); 200 } else { 201 id = 0; 202 } 203 204 /* Save ID for later use */ 205 priv->synopsys_id = id; 206 207 /* Lets assume some safe values first */ 208 priv->ptpaddr = priv->ioaddr + 209 (needs_gmac4 ? PTP_GMAC4_OFFSET : PTP_GMAC3_X_OFFSET); 210 priv->mmcaddr = priv->ioaddr + 211 (needs_gmac4 ? MMC_GMAC4_OFFSET : MMC_GMAC3_X_OFFSET); 212 213 /* Check for HW specific setup first */ 214 if (priv->plat->setup) { 215 mac = priv->plat->setup(priv); 216 needs_setup = false; 217 } else { 218 mac = devm_kzalloc(priv->device, sizeof(*mac), GFP_KERNEL); 219 } 220 221 if (!mac) 222 return -ENOMEM; 223 224 /* Fallback to generic HW */ 225 for (i = ARRAY_SIZE(stmmac_hw) - 1; i >= 0; i--) { 226 entry = &stmmac_hw[i]; 227 228 if (needs_gmac ^ entry->gmac) 229 continue; 230 if (needs_gmac4 ^ entry->gmac4) 231 continue; 232 /* Use synopsys_id var because some setups can override this */ 233 if (priv->synopsys_id < entry->min_id) 234 continue; 235 236 /* Only use generic HW helpers if needed */ 237 mac->desc = mac->desc ? : entry->desc; 238 mac->dma = mac->dma ? : entry->dma; 239 mac->mac = mac->mac ? : entry->mac; 240 mac->ptp = mac->ptp ? : entry->hwtimestamp; 241 mac->mode = mac->mode ? : entry->mode; 242 mac->tc = mac->tc ? : entry->tc; 243 244 priv->hw = mac; 245 priv->ptpaddr = priv->ioaddr + entry->regs.ptp_off; 246 priv->mmcaddr = priv->ioaddr + entry->regs.mmc_off; 247 248 /* Entry found */ 249 if (needs_setup) { 250 ret = entry->setup(priv); 251 if (ret) 252 return ret; 253 } 254 255 /* Save quirks, if needed for posterior use */ 256 priv->hwif_quirks = entry->quirks; 257 return 0; 258 } 259 260 dev_err(priv->device, "Failed to find HW IF (id=0x%x, gmac=%d/%d)\n", 261 id, needs_gmac, needs_gmac4); 262 return -EINVAL; 263 } 264