linux/drivers/net/ethernet/stmicro/stmmac/hwif.c
<<
>>
Prefs
   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
  11static 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
  26static u32 stmmac_get_dev_id(struct stmmac_priv *priv, u32 id_reg)
  27{
  28        u32 reg = readl(priv->ioaddr + id_reg);
  29
  30        if (!reg) {
  31                dev_info(priv->device, "Version ID not available\n");
  32                return 0x0;
  33        }
  34
  35        return (reg & GENMASK(15, 8)) >> 8;
  36}
  37
  38static void stmmac_dwmac_mode_quirk(struct stmmac_priv *priv)
  39{
  40        struct mac_device_info *mac = priv->hw;
  41
  42        if (priv->chain_mode) {
  43                dev_info(priv->device, "Chain mode enabled\n");
  44                priv->mode = STMMAC_CHAIN_MODE;
  45                mac->mode = &chain_mode_ops;
  46        } else {
  47                dev_info(priv->device, "Ring mode enabled\n");
  48                priv->mode = STMMAC_RING_MODE;
  49                mac->mode = &ring_mode_ops;
  50        }
  51}
  52
  53static int stmmac_dwmac1_quirks(struct stmmac_priv *priv)
  54{
  55        struct mac_device_info *mac = priv->hw;
  56
  57        if (priv->plat->enh_desc) {
  58                dev_info(priv->device, "Enhanced/Alternate descriptors\n");
  59
  60                /* GMAC older than 3.50 has no extended descriptors */
  61                if (priv->synopsys_id >= DWMAC_CORE_3_50) {
  62                        dev_info(priv->device, "Enabled extended descriptors\n");
  63                        priv->extend_desc = 1;
  64                } else {
  65                        dev_warn(priv->device, "Extended descriptors not supported\n");
  66                }
  67
  68                mac->desc = &enh_desc_ops;
  69        } else {
  70                dev_info(priv->device, "Normal descriptors\n");
  71                mac->desc = &ndesc_ops;
  72        }
  73
  74        stmmac_dwmac_mode_quirk(priv);
  75        return 0;
  76}
  77
  78static int stmmac_dwmac4_quirks(struct stmmac_priv *priv)
  79{
  80        stmmac_dwmac_mode_quirk(priv);
  81        return 0;
  82}
  83
  84static int stmmac_dwxlgmac_quirks(struct stmmac_priv *priv)
  85{
  86        priv->hw->xlgmac = true;
  87        return 0;
  88}
  89
  90static const struct stmmac_hwif_entry {
  91        bool gmac;
  92        bool gmac4;
  93        bool xgmac;
  94        u32 min_id;
  95        u32 dev_id;
  96        const struct stmmac_regs_off regs;
  97        const void *desc;
  98        const void *dma;
  99        const void *mac;
 100        const void *hwtimestamp;
 101        const void *mode;
 102        const void *tc;
 103        const void *mmc;
 104        int (*setup)(struct stmmac_priv *priv);
 105        int (*quirks)(struct stmmac_priv *priv);
 106} stmmac_hw[] = {
 107        /* NOTE: New HW versions shall go to the end of this table */
 108        {
 109                .gmac = false,
 110                .gmac4 = false,
 111                .xgmac = false,
 112                .min_id = 0,
 113                .regs = {
 114                        .ptp_off = PTP_GMAC3_X_OFFSET,
 115                        .mmc_off = MMC_GMAC3_X_OFFSET,
 116                },
 117                .desc = NULL,
 118                .dma = &dwmac100_dma_ops,
 119                .mac = &dwmac100_ops,
 120                .hwtimestamp = &stmmac_ptp,
 121                .mode = NULL,
 122                .tc = NULL,
 123                .mmc = &dwmac_mmc_ops,
 124                .setup = dwmac100_setup,
 125                .quirks = stmmac_dwmac1_quirks,
 126        }, {
 127                .gmac = true,
 128                .gmac4 = false,
 129                .xgmac = false,
 130                .min_id = 0,
 131                .regs = {
 132                        .ptp_off = PTP_GMAC3_X_OFFSET,
 133                        .mmc_off = MMC_GMAC3_X_OFFSET,
 134                },
 135                .desc = NULL,
 136                .dma = &dwmac1000_dma_ops,
 137                .mac = &dwmac1000_ops,
 138                .hwtimestamp = &stmmac_ptp,
 139                .mode = NULL,
 140                .tc = NULL,
 141                .mmc = &dwmac_mmc_ops,
 142                .setup = dwmac1000_setup,
 143                .quirks = stmmac_dwmac1_quirks,
 144        }, {
 145                .gmac = false,
 146                .gmac4 = true,
 147                .xgmac = false,
 148                .min_id = 0,
 149                .regs = {
 150                        .ptp_off = PTP_GMAC4_OFFSET,
 151                        .mmc_off = MMC_GMAC4_OFFSET,
 152                },
 153                .desc = &dwmac4_desc_ops,
 154                .dma = &dwmac4_dma_ops,
 155                .mac = &dwmac4_ops,
 156                .hwtimestamp = &stmmac_ptp,
 157                .mode = NULL,
 158                .tc = &dwmac510_tc_ops,
 159                .mmc = &dwmac_mmc_ops,
 160                .setup = dwmac4_setup,
 161                .quirks = stmmac_dwmac4_quirks,
 162        }, {
 163                .gmac = false,
 164                .gmac4 = true,
 165                .xgmac = false,
 166                .min_id = DWMAC_CORE_4_00,
 167                .regs = {
 168                        .ptp_off = PTP_GMAC4_OFFSET,
 169                        .mmc_off = MMC_GMAC4_OFFSET,
 170                },
 171                .desc = &dwmac4_desc_ops,
 172                .dma = &dwmac4_dma_ops,
 173                .mac = &dwmac410_ops,
 174                .hwtimestamp = &stmmac_ptp,
 175                .mode = &dwmac4_ring_mode_ops,
 176                .tc = &dwmac510_tc_ops,
 177                .mmc = &dwmac_mmc_ops,
 178                .setup = dwmac4_setup,
 179                .quirks = NULL,
 180        }, {
 181                .gmac = false,
 182                .gmac4 = true,
 183                .xgmac = false,
 184                .min_id = DWMAC_CORE_4_10,
 185                .regs = {
 186                        .ptp_off = PTP_GMAC4_OFFSET,
 187                        .mmc_off = MMC_GMAC4_OFFSET,
 188                },
 189                .desc = &dwmac4_desc_ops,
 190                .dma = &dwmac410_dma_ops,
 191                .mac = &dwmac410_ops,
 192                .hwtimestamp = &stmmac_ptp,
 193                .mode = &dwmac4_ring_mode_ops,
 194                .tc = &dwmac510_tc_ops,
 195                .mmc = &dwmac_mmc_ops,
 196                .setup = dwmac4_setup,
 197                .quirks = NULL,
 198        }, {
 199                .gmac = false,
 200                .gmac4 = true,
 201                .xgmac = false,
 202                .min_id = DWMAC_CORE_5_10,
 203                .regs = {
 204                        .ptp_off = PTP_GMAC4_OFFSET,
 205                        .mmc_off = MMC_GMAC4_OFFSET,
 206                },
 207                .desc = &dwmac4_desc_ops,
 208                .dma = &dwmac410_dma_ops,
 209                .mac = &dwmac510_ops,
 210                .hwtimestamp = &stmmac_ptp,
 211                .mode = &dwmac4_ring_mode_ops,
 212                .tc = &dwmac510_tc_ops,
 213                .mmc = &dwmac_mmc_ops,
 214                .setup = dwmac4_setup,
 215                .quirks = NULL,
 216        }, {
 217                .gmac = false,
 218                .gmac4 = false,
 219                .xgmac = true,
 220                .min_id = DWXGMAC_CORE_2_10,
 221                .dev_id = DWXGMAC_ID,
 222                .regs = {
 223                        .ptp_off = PTP_XGMAC_OFFSET,
 224                        .mmc_off = MMC_XGMAC_OFFSET,
 225                },
 226                .desc = &dwxgmac210_desc_ops,
 227                .dma = &dwxgmac210_dma_ops,
 228                .mac = &dwxgmac210_ops,
 229                .hwtimestamp = &stmmac_ptp,
 230                .mode = NULL,
 231                .tc = &dwmac510_tc_ops,
 232                .mmc = &dwxgmac_mmc_ops,
 233                .setup = dwxgmac2_setup,
 234                .quirks = NULL,
 235        }, {
 236                .gmac = false,
 237                .gmac4 = false,
 238                .xgmac = true,
 239                .min_id = DWXLGMAC_CORE_2_00,
 240                .dev_id = DWXLGMAC_ID,
 241                .regs = {
 242                        .ptp_off = PTP_XGMAC_OFFSET,
 243                        .mmc_off = MMC_XGMAC_OFFSET,
 244                },
 245                .desc = &dwxgmac210_desc_ops,
 246                .dma = &dwxgmac210_dma_ops,
 247                .mac = &dwxlgmac2_ops,
 248                .hwtimestamp = &stmmac_ptp,
 249                .mode = NULL,
 250                .tc = &dwmac510_tc_ops,
 251                .mmc = &dwxgmac_mmc_ops,
 252                .setup = dwxlgmac2_setup,
 253                .quirks = stmmac_dwxlgmac_quirks,
 254        },
 255};
 256
 257int stmmac_hwif_init(struct stmmac_priv *priv)
 258{
 259        bool needs_xgmac = priv->plat->has_xgmac;
 260        bool needs_gmac4 = priv->plat->has_gmac4;
 261        bool needs_gmac = priv->plat->has_gmac;
 262        const struct stmmac_hwif_entry *entry;
 263        struct mac_device_info *mac;
 264        bool needs_setup = true;
 265        u32 id, dev_id = 0;
 266        int i, ret;
 267
 268        if (needs_gmac) {
 269                id = stmmac_get_id(priv, GMAC_VERSION);
 270        } else if (needs_gmac4 || needs_xgmac) {
 271                id = stmmac_get_id(priv, GMAC4_VERSION);
 272                if (needs_xgmac)
 273                        dev_id = stmmac_get_dev_id(priv, GMAC4_VERSION);
 274        } else {
 275                id = 0;
 276        }
 277
 278        /* Save ID for later use */
 279        priv->synopsys_id = id;
 280
 281        /* Lets assume some safe values first */
 282        priv->ptpaddr = priv->ioaddr +
 283                (needs_gmac4 ? PTP_GMAC4_OFFSET : PTP_GMAC3_X_OFFSET);
 284        priv->mmcaddr = priv->ioaddr +
 285                (needs_gmac4 ? MMC_GMAC4_OFFSET : MMC_GMAC3_X_OFFSET);
 286
 287        /* Check for HW specific setup first */
 288        if (priv->plat->setup) {
 289                mac = priv->plat->setup(priv);
 290                needs_setup = false;
 291        } else {
 292                mac = devm_kzalloc(priv->device, sizeof(*mac), GFP_KERNEL);
 293        }
 294
 295        if (!mac)
 296                return -ENOMEM;
 297
 298        /* Fallback to generic HW */
 299        for (i = ARRAY_SIZE(stmmac_hw) - 1; i >= 0; i--) {
 300                entry = &stmmac_hw[i];
 301
 302                if (needs_gmac ^ entry->gmac)
 303                        continue;
 304                if (needs_gmac4 ^ entry->gmac4)
 305                        continue;
 306                if (needs_xgmac ^ entry->xgmac)
 307                        continue;
 308                /* Use synopsys_id var because some setups can override this */
 309                if (priv->synopsys_id < entry->min_id)
 310                        continue;
 311                if (needs_xgmac && (dev_id ^ entry->dev_id))
 312                        continue;
 313
 314                /* Only use generic HW helpers if needed */
 315                mac->desc = mac->desc ? : entry->desc;
 316                mac->dma = mac->dma ? : entry->dma;
 317                mac->mac = mac->mac ? : entry->mac;
 318                mac->ptp = mac->ptp ? : entry->hwtimestamp;
 319                mac->mode = mac->mode ? : entry->mode;
 320                mac->tc = mac->tc ? : entry->tc;
 321                mac->mmc = mac->mmc ? : entry->mmc;
 322
 323                priv->hw = mac;
 324                priv->ptpaddr = priv->ioaddr + entry->regs.ptp_off;
 325                priv->mmcaddr = priv->ioaddr + entry->regs.mmc_off;
 326
 327                /* Entry found */
 328                if (needs_setup) {
 329                        ret = entry->setup(priv);
 330                        if (ret)
 331                                return ret;
 332                }
 333
 334                /* Save quirks, if needed for posterior use */
 335                priv->hwif_quirks = entry->quirks;
 336                return 0;
 337        }
 338
 339        dev_err(priv->device, "Failed to find HW IF (id=0x%x, gmac=%d/%d)\n",
 340                        id, needs_gmac, needs_gmac4);
 341        return -EINVAL;
 342}
 343