uboot/drivers/net/octeontx/smi.c
<<
>>
Prefs
   1// SPDX-License-Identifier:    GPL-2.0
   2/*
   3 * Copyright (C) 2018 Marvell International Ltd.
   4 */
   5
   6#include <dm.h>
   7#include <malloc.h>
   8#include <miiphy.h>
   9#include <misc.h>
  10#include <pci.h>
  11#include <pci_ids.h>
  12#include <phy.h>
  13#include <asm/global_data.h>
  14#include <asm/io.h>
  15#include <linux/ctype.h>
  16#include <linux/delay.h>
  17
  18#define PCI_DEVICE_ID_OCTEONTX_SMI 0xA02B
  19
  20DECLARE_GLOBAL_DATA_PTR;
  21
  22enum octeontx_smi_mode {
  23        CLAUSE22 = 0,
  24        CLAUSE45 = 1,
  25};
  26
  27enum {
  28        SMI_OP_C22_WRITE = 0,
  29        SMI_OP_C22_READ = 1,
  30
  31        SMI_OP_C45_ADDR = 0,
  32        SMI_OP_C45_WRITE = 1,
  33        SMI_OP_C45_PRIA = 2,
  34        SMI_OP_C45_READ = 3,
  35};
  36
  37union smi_x_clk {
  38        u64 u;
  39        struct smi_x_clk_s {
  40                int phase:8;
  41                int sample:4;
  42                int preamble:1;
  43                int clk_idle:1;
  44                int reserved_14_14:1;
  45                int sample_mode:1;
  46                int sample_hi:5;
  47                int reserved_21_23:3;
  48                int mode:1;
  49        } s;
  50};
  51
  52union smi_x_cmd {
  53        u64 u;
  54        struct smi_x_cmd_s {
  55                int reg_adr:5;
  56                int reserved_5_7:3;
  57                int phy_adr:5;
  58                int reserved_13_15:3;
  59                int phy_op:2;
  60        } s;
  61};
  62
  63union smi_x_wr_dat {
  64        u64 u;
  65        struct smi_x_wr_dat_s {
  66                unsigned int dat:16;
  67                int val:1;
  68                int pending:1;
  69        } s;
  70};
  71
  72union smi_x_rd_dat {
  73        u64 u;
  74        struct smi_x_rd_dat_s {
  75                unsigned int dat:16;
  76                int val:1;
  77                int pending:1;
  78        } s;
  79};
  80
  81union smi_x_en {
  82        u64 u;
  83        struct smi_x_en_s {
  84                int en:1;
  85        } s;
  86};
  87
  88#define SMI_X_RD_DAT    0x10ull
  89#define SMI_X_WR_DAT    0x08ull
  90#define SMI_X_CMD       0x00ull
  91#define SMI_X_CLK       0x18ull
  92#define SMI_X_EN        0x20ull
  93
  94struct octeontx_smi_priv {
  95        void __iomem *baseaddr;
  96        enum octeontx_smi_mode mode;
  97};
  98
  99#define MDIO_TIMEOUT 10000
 100
 101void octeontx_smi_setmode(struct mii_dev *bus, enum octeontx_smi_mode mode)
 102{
 103        struct octeontx_smi_priv *priv = bus->priv;
 104        union smi_x_clk smix_clk;
 105
 106        smix_clk.u = readq(priv->baseaddr + SMI_X_CLK);
 107        smix_clk.s.mode = mode;
 108        smix_clk.s.preamble = mode == CLAUSE45;
 109        writeq(smix_clk.u, priv->baseaddr + SMI_X_CLK);
 110
 111        priv->mode = mode;
 112}
 113
 114int octeontx_c45_addr(struct mii_dev *bus, int addr, int devad, int regnum)
 115{
 116        struct octeontx_smi_priv *priv = bus->priv;
 117
 118        union smi_x_cmd smix_cmd;
 119        union smi_x_wr_dat smix_wr_dat;
 120        unsigned long timeout = MDIO_TIMEOUT;
 121
 122        smix_wr_dat.u = 0;
 123        smix_wr_dat.s.dat = regnum;
 124
 125        writeq(smix_wr_dat.u, priv->baseaddr + SMI_X_WR_DAT);
 126
 127        smix_cmd.u = 0;
 128        smix_cmd.s.phy_op = SMI_OP_C45_ADDR;
 129        smix_cmd.s.phy_adr = addr;
 130        smix_cmd.s.reg_adr = devad;
 131
 132        writeq(smix_cmd.u, priv->baseaddr + SMI_X_CMD);
 133
 134        do {
 135                smix_wr_dat.u = readq(priv->baseaddr + SMI_X_WR_DAT);
 136                udelay(100);
 137                timeout--;
 138        } while (smix_wr_dat.s.pending && timeout);
 139
 140        return timeout == 0;
 141}
 142
 143int octeontx_phy_read(struct mii_dev *bus, int addr, int devad, int regnum)
 144{
 145        struct octeontx_smi_priv *priv = bus->priv;
 146        union smi_x_cmd smix_cmd;
 147        union smi_x_rd_dat smix_rd_dat;
 148        unsigned long timeout = MDIO_TIMEOUT;
 149        int ret;
 150
 151        enum octeontx_smi_mode mode = (devad < 0) ? CLAUSE22 : CLAUSE45;
 152
 153        debug("RD: Mode: %u, baseaddr: %p, addr: %d, devad: %d, reg: %d\n",
 154              mode, priv->baseaddr, addr, devad, regnum);
 155
 156        octeontx_smi_setmode(bus, mode);
 157
 158        if (mode == CLAUSE45) {
 159                ret = octeontx_c45_addr(bus, addr, devad, regnum);
 160
 161                debug("RD: ret: %u\n", ret);
 162
 163                if (ret)
 164                        return 0;
 165        }
 166
 167        smix_cmd.u = 0;
 168        smix_cmd.s.phy_adr = addr;
 169
 170        if (mode == CLAUSE45) {
 171                smix_cmd.s.reg_adr = devad;
 172                smix_cmd.s.phy_op = SMI_OP_C45_READ;
 173        } else {
 174                smix_cmd.s.reg_adr = regnum;
 175                smix_cmd.s.phy_op = SMI_OP_C22_READ;
 176        }
 177
 178        writeq(smix_cmd.u, priv->baseaddr + SMI_X_CMD);
 179
 180        do {
 181                smix_rd_dat.u = readq(priv->baseaddr + SMI_X_RD_DAT);
 182                udelay(10);
 183                timeout--;
 184        } while (smix_rd_dat.s.pending && timeout);
 185
 186        debug("SMIX_RD_DAT: %lx\n", (unsigned long)smix_rd_dat.u);
 187
 188        return smix_rd_dat.s.dat;
 189}
 190
 191int octeontx_phy_write(struct mii_dev *bus, int addr, int devad, int regnum,
 192                       u16 value)
 193{
 194        struct octeontx_smi_priv *priv = bus->priv;
 195        union smi_x_cmd smix_cmd;
 196        union smi_x_wr_dat smix_wr_dat;
 197        unsigned long timeout = MDIO_TIMEOUT;
 198        int ret;
 199
 200        enum octeontx_smi_mode mode = (devad < 0) ? CLAUSE22 : CLAUSE45;
 201
 202        debug("WR: Mode: %u, baseaddr: %p, addr: %d, devad: %d, reg: %d\n",
 203              mode, priv->baseaddr, addr, devad, regnum);
 204
 205        if (mode == CLAUSE45) {
 206                ret = octeontx_c45_addr(bus, addr, devad, regnum);
 207
 208                debug("WR: ret: %u\n", ret);
 209
 210                if (ret)
 211                        return ret;
 212        }
 213
 214        smix_wr_dat.u = 0;
 215        smix_wr_dat.s.dat = value;
 216
 217        writeq(smix_wr_dat.u, priv->baseaddr + SMI_X_WR_DAT);
 218
 219        smix_cmd.u = 0;
 220        smix_cmd.s.phy_adr = addr;
 221
 222        if (mode == CLAUSE45) {
 223                smix_cmd.s.reg_adr = devad;
 224                smix_cmd.s.phy_op = SMI_OP_C45_WRITE;
 225        } else {
 226                smix_cmd.s.reg_adr = regnum;
 227                smix_cmd.s.phy_op = SMI_OP_C22_WRITE;
 228        }
 229
 230        writeq(smix_cmd.u, priv->baseaddr + SMI_X_CMD);
 231
 232        do {
 233                smix_wr_dat.u = readq(priv->baseaddr + SMI_X_WR_DAT);
 234                udelay(10);
 235                timeout--;
 236        } while (smix_wr_dat.s.pending && timeout);
 237
 238        debug("SMIX_WR_DAT: %lx\n", (unsigned long)smix_wr_dat.u);
 239
 240        return timeout == 0;
 241}
 242
 243int octeontx_smi_reset(struct mii_dev *bus)
 244{
 245        struct octeontx_smi_priv *priv = bus->priv;
 246
 247        union smi_x_en smi_en;
 248
 249        smi_en.s.en = 0;
 250        writeq(smi_en.u, priv->baseaddr + SMI_X_EN);
 251
 252        smi_en.s.en = 1;
 253        writeq(smi_en.u, priv->baseaddr + SMI_X_EN);
 254
 255        octeontx_smi_setmode(bus, CLAUSE22);
 256
 257        return 0;
 258}
 259
 260/* PHY XS initialization, primarily for RXAUI
 261 *
 262 */
 263int rxaui_phy_xs_init(struct mii_dev *bus, int phy_addr)
 264{
 265        int reg;
 266        ulong start_time;
 267        int phy_id1, phy_id2;
 268        int oui, model_number;
 269
 270        phy_id1 = octeontx_phy_read(bus, phy_addr, 1, 0x2);
 271        phy_id2 = octeontx_phy_read(bus, phy_addr, 1, 0x3);
 272        model_number = (phy_id2 >> 4) & 0x3F;
 273        debug("%s model %x\n", __func__, model_number);
 274        oui = phy_id1;
 275        oui <<= 6;
 276        oui |= (phy_id2 >> 10) & 0x3F;
 277        debug("%s oui %x\n", __func__, oui);
 278        switch (oui) {
 279        case 0x5016:
 280                if (model_number == 9) {
 281                        debug("%s +\n", __func__);
 282                        /* Perform hardware reset in XGXS control */
 283                        reg = octeontx_phy_read(bus, phy_addr, 4, 0x0);
 284                        if ((reg & 0xffff) < 0)
 285                                goto read_error;
 286                        reg |= 0x8000;
 287                        octeontx_phy_write(bus, phy_addr, 4, 0x0, reg);
 288
 289                        start_time = get_timer(0);
 290                        do {
 291                                reg = octeontx_phy_read(bus, phy_addr, 4, 0x0);
 292                                if ((reg & 0xffff) < 0)
 293                                        goto read_error;
 294                        } while ((reg & 0x8000) && get_timer(start_time) < 500);
 295                        if (reg & 0x8000) {
 296                                printf("HW reset for M88X3120 PHY failed");
 297                                printf("MII_BMCR: 0x%x\n", reg);
 298                                return -1;
 299                        }
 300                        /* program 4.49155 with 0x5 */
 301                        octeontx_phy_write(bus, phy_addr, 4, 0xc003, 0x5);
 302                }
 303                break;
 304        default:
 305                break;
 306        }
 307
 308        return 0;
 309
 310read_error:
 311        debug("M88X3120 PHY config read failed\n");
 312        return -1;
 313}
 314
 315int octeontx_smi_probe(struct udevice *dev)
 316{
 317        pci_dev_t bdf = dm_pci_get_bdf(dev);
 318        struct octeontx_smi_priv *priv;
 319        struct mii_dev *bus;
 320        int ret, cnt = 0;
 321        ofnode subnode;
 322        u64 baseaddr;
 323
 324        debug("SMI PCI device: %x\n", bdf);
 325        if (!dm_pci_map_bar(dev, PCI_BASE_ADDRESS_0, PCI_REGION_MEM)) {
 326                printf("Failed to map PCI region for bdf %x\n", bdf);
 327                return -1;
 328        }
 329
 330        dev_for_each_subnode(subnode, dev) {
 331                if (!ofnode_device_is_compatible(subnode,
 332                                                 "cavium,thunder-8890-mdio"))
 333                        continue;
 334                if (ofnode_read_u64(subnode, "reg", &baseaddr))
 335                        continue;
 336                bus = mdio_alloc();
 337                priv = malloc(sizeof(*priv));
 338                if (!bus || !priv) {
 339                        printf("Failed to allocate OcteonTX MDIO bus # %u\n",
 340                               dev_seq(dev));
 341                        return -1;
 342                }
 343
 344                bus->read = octeontx_phy_read;
 345                bus->write = octeontx_phy_write;
 346                bus->reset = octeontx_smi_reset;
 347                bus->priv = priv;
 348
 349                priv->mode = CLAUSE22;
 350                priv->baseaddr = (void __iomem *)baseaddr;
 351                debug("mdio base addr %p\n", priv->baseaddr);
 352
 353                /* use given name or generate its own unique name */
 354                snprintf(bus->name, MDIO_NAME_LEN, "smi%d", cnt++);
 355
 356                ret = mdio_register(bus);
 357                if (ret)
 358                        return ret;
 359        }
 360        return 0;
 361}
 362
 363static const struct udevice_id octeontx_smi_ids[] = {
 364        { .compatible = "cavium,thunder-8890-mdio-nexus" },
 365        {}
 366};
 367
 368U_BOOT_DRIVER(octeontx_smi) = {
 369        .name   = "octeontx_smi",
 370        .id     = UCLASS_MISC,
 371        .probe  = octeontx_smi_probe,
 372        .of_match = octeontx_smi_ids,
 373};
 374
 375static struct pci_device_id octeontx_smi_supported[] = {
 376        { PCI_VDEVICE(CAVIUM, PCI_DEVICE_ID_CAVIUM_SMI) },
 377        {}
 378};
 379
 380U_BOOT_PCI_DEVICE(octeontx_smi, octeontx_smi_supported);
 381