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