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, ®); 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 ®); 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, ®); 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 ®); 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, ®); 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 ®); 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