uboot/drivers/net/phy/mv88e6352.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * (C) Copyright 2012
   4 * Valentin Lontgchamp, Keymile AG, valentin.longchamp@keymile.com
   5 */
   6
   7#include <common.h>
   8#include <command.h>
   9#include <log.h>
  10#include <miiphy.h>
  11#include <linux/delay.h>
  12#include <linux/errno.h>
  13#include <mv88e6352.h>
  14
  15#define SMI_HDR         ((0x8 | 0x1) << 12)
  16#define SMI_BUSY_MASK   (0x8000)
  17#define SMIRD_OP        (0x2 << 10)
  18#define SMIWR_OP        (0x1 << 10)
  19#define SMI_MASK        0x1f
  20#define PORT_SHIFT      5
  21
  22#define COMMAND_REG     0
  23#define DATA_REG        1
  24
  25/* global registers */
  26#define GLOBAL          0x1b
  27
  28#define GLOBAL_STATUS   0x00
  29#define PPU_STATE       0x8000
  30
  31#define GLOBAL_CTRL     0x04
  32#define SW_RESET        0x8000
  33#define PPU_ENABLE      0x4000
  34
  35static int sw_wait_rdy(const char *devname, u8 phy_addr)
  36{
  37        u16 command;
  38        u32 timeout = 100;
  39        int ret;
  40
  41        /* wait till the SMI is not busy */
  42        do {
  43                /* read command register */
  44                ret = miiphy_read(devname, phy_addr, COMMAND_REG, &command);
  45                if (ret < 0) {
  46                        printf("%s: Error reading command register\n",
  47                                __func__);
  48                        return ret;
  49                }
  50                if (timeout-- == 0) {
  51                        printf("Err..(%s) SMI busy timeout\n", __func__);
  52                        return -EFAULT;
  53                }
  54        } while (command & SMI_BUSY_MASK);
  55
  56        return 0;
  57}
  58
  59static int sw_reg_read(const char *devname, u8 phy_addr, u8 port,
  60        u8 reg, u16 *data)
  61{
  62        int ret;
  63        u16 command;
  64
  65        ret = sw_wait_rdy(devname, phy_addr);
  66        if (ret)
  67                return ret;
  68
  69        command = SMI_HDR | SMIRD_OP | ((port&SMI_MASK) << PORT_SHIFT) |
  70                        (reg & SMI_MASK);
  71        debug("%s: write to command: %#x\n", __func__, command);
  72        ret = miiphy_write(devname, phy_addr, COMMAND_REG, command);
  73        if (ret)
  74                return ret;
  75
  76        ret = sw_wait_rdy(devname, phy_addr);
  77        if (ret)
  78                return ret;
  79
  80        ret = miiphy_read(devname, phy_addr, DATA_REG, data);
  81
  82        return ret;
  83}
  84
  85static int sw_reg_write(const char *devname, u8 phy_addr, u8 port,
  86        u8 reg, u16 data)
  87{
  88        int ret;
  89        u16 value;
  90
  91        ret = sw_wait_rdy(devname, phy_addr);
  92        if (ret)
  93                return ret;
  94
  95        debug("%s: write to data: %#x\n", __func__, data);
  96        ret = miiphy_write(devname, phy_addr, DATA_REG, data);
  97        if (ret)
  98                return ret;
  99
 100        value = SMI_HDR | SMIWR_OP | ((port & SMI_MASK) << PORT_SHIFT) |
 101                        (reg & SMI_MASK);
 102        debug("%s: write to command: %#x\n", __func__, value);
 103        ret = miiphy_write(devname, phy_addr, COMMAND_REG, value);
 104        if (ret)
 105                return ret;
 106
 107        ret = sw_wait_rdy(devname, phy_addr);
 108        if (ret)
 109                return ret;
 110
 111        return 0;
 112}
 113
 114static int ppu_enable(const char *devname, u8 phy_addr)
 115{
 116        int i, ret = 0;
 117        u16 reg;
 118
 119        ret = sw_reg_read(devname, phy_addr, GLOBAL, GLOBAL_CTRL, &reg);
 120        if (ret) {
 121                printf("%s: Error reading global ctrl reg\n", __func__);
 122                return ret;
 123        }
 124
 125        reg |= PPU_ENABLE;
 126
 127        ret = sw_reg_write(devname, phy_addr, GLOBAL, GLOBAL_CTRL, reg);
 128        if (ret) {
 129                printf("%s: Error writing global ctrl reg\n", __func__);
 130                return ret;
 131        }
 132
 133        for (i = 0; i < 1000; i++) {
 134                sw_reg_read(devname, phy_addr, GLOBAL, GLOBAL_STATUS,
 135                        &reg);
 136                if ((reg & 0xc000) == 0xc000)
 137                        return 0;
 138                udelay(1000);
 139        }
 140
 141        return -ETIMEDOUT;
 142}
 143
 144static int ppu_disable(const char *devname, u8 phy_addr)
 145{
 146        int i, ret = 0;
 147        u16 reg;
 148
 149        ret = sw_reg_read(devname, phy_addr, GLOBAL, GLOBAL_CTRL, &reg);
 150        if (ret) {
 151                printf("%s: Error reading global ctrl reg\n", __func__);
 152                return ret;
 153        }
 154
 155        reg &= ~PPU_ENABLE;
 156
 157        ret = sw_reg_write(devname, phy_addr, GLOBAL, GLOBAL_CTRL, reg);
 158        if (ret) {
 159                printf("%s: Error writing global ctrl reg\n", __func__);
 160                return ret;
 161        }
 162
 163        for (i = 0; i < 1000; i++) {
 164                sw_reg_read(devname, phy_addr, GLOBAL, GLOBAL_STATUS,
 165                        &reg);
 166                if ((reg & 0xc000) != 0xc000)
 167                        return 0;
 168                udelay(1000);
 169        }
 170
 171        return -ETIMEDOUT;
 172}
 173
 174int mv88e_sw_program(const char *devname, u8 phy_addr,
 175        struct mv88e_sw_reg *regs, int regs_nb)
 176{
 177        int i, ret = 0;
 178
 179        /* first we need to disable the PPU */
 180        ret = ppu_disable(devname, phy_addr);
 181        if (ret) {
 182                printf("%s: Error disabling PPU\n", __func__);
 183                return ret;
 184        }
 185
 186        for (i = 0; i < regs_nb; i++) {
 187                ret = sw_reg_write(devname, phy_addr, regs[i].port,
 188                        regs[i].reg, regs[i].value);
 189                if (ret) {
 190                        printf("%s: Error configuring switch\n", __func__);
 191                        ppu_enable(devname, phy_addr);
 192                        return ret;
 193                }
 194        }
 195
 196        /* re-enable the PPU */
 197        ret = ppu_enable(devname, phy_addr);
 198        if (ret) {
 199                printf("%s: Error enabling PPU\n", __func__);
 200                return ret;
 201        }
 202
 203        return 0;
 204}
 205
 206int mv88e_sw_reset(const char *devname, u8 phy_addr)
 207{
 208        int i, ret = 0;
 209        u16 reg;
 210
 211        ret = sw_reg_read(devname, phy_addr, GLOBAL, GLOBAL_CTRL, &reg);
 212        if (ret) {
 213                printf("%s: Error reading global ctrl reg\n", __func__);
 214                return ret;
 215        }
 216
 217        reg = SW_RESET | PPU_ENABLE | 0x0400;
 218
 219        ret = sw_reg_write(devname, phy_addr, GLOBAL, GLOBAL_CTRL, reg);
 220        if (ret) {
 221                printf("%s: Error writing global ctrl reg\n", __func__);
 222                return ret;
 223        }
 224
 225        for (i = 0; i < 1000; i++) {
 226                sw_reg_read(devname, phy_addr, GLOBAL, GLOBAL_STATUS,
 227                        &reg);
 228                if ((reg & 0xc800) != 0xc800)
 229                        return 0;
 230                udelay(1000);
 231        }
 232
 233        return -ETIMEDOUT;
 234}
 235
 236int do_mvsw_reg_read(const char *name, int argc, char *const argv[])
 237{
 238        u16 value = 0, phyaddr, reg, port;
 239        int ret;
 240
 241        phyaddr = dectoul(argv[1], NULL);
 242        port = dectoul(argv[2], NULL);
 243        reg = dectoul(argv[3], NULL);
 244
 245        ret = sw_reg_read(name, phyaddr, port, reg, &value);
 246        printf("%#x\n", value);
 247
 248        return ret;
 249}
 250
 251int do_mvsw_reg_write(const char *name, int argc, char *const argv[])
 252{
 253        u16 value = 0, phyaddr, reg, port;
 254        int ret;
 255
 256        phyaddr = dectoul(argv[1], NULL);
 257        port = dectoul(argv[2], NULL);
 258        reg = dectoul(argv[3], NULL);
 259        value = hextoul(argv[4], NULL);
 260
 261        ret = sw_reg_write(name, phyaddr, port, reg, value);
 262
 263        return ret;
 264}
 265
 266
 267int do_mvsw_reg(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
 268{
 269        int ret;
 270        const char *cmd, *ethname;
 271
 272        if (argc < 2)
 273                return cmd_usage(cmdtp);
 274
 275        cmd = argv[1];
 276        --argc;
 277        ++argv;
 278
 279        if (strcmp(cmd, "read") == 0) {
 280                if (argc < 5)
 281                        return cmd_usage(cmdtp);
 282                ethname = argv[1];
 283                --argc;
 284                ++argv;
 285                ret = do_mvsw_reg_read(ethname, argc, argv);
 286        } else if (strcmp(cmd, "write") == 0) {
 287                if (argc < 6)
 288                        return cmd_usage(cmdtp);
 289                ethname = argv[1];
 290                --argc;
 291                ++argv;
 292                ret = do_mvsw_reg_write(ethname, argc, argv);
 293        } else
 294                return cmd_usage(cmdtp);
 295
 296        return ret;
 297}
 298
 299U_BOOT_CMD(
 300        mvsw_reg,       7,      1,      do_mvsw_reg,
 301        "marvell 88e6352 switch register access",
 302        "write ethname phyaddr port reg value\n"
 303        "mvsw_reg read  ethname phyaddr port reg\n"
 304        );
 305