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 32 static 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 56 static 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 82 static 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 111 static 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, ®); 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 ®); 133 if ((reg & 0xc000) == 0xc000) 134 return 0; 135 udelay(1000); 136 } 137 138 return -ETIMEDOUT; 139 } 140 141 static 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, ®); 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 ®); 163 if ((reg & 0xc000) != 0xc000) 164 return 0; 165 udelay(1000); 166 } 167 168 return -ETIMEDOUT; 169 } 170 171 int 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 203 int 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, ®); 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 ®); 225 if ((reg & 0xc800) != 0xc800) 226 return 0; 227 udelay(1000); 228 } 229 230 return -ETIMEDOUT; 231 } 232 233 int 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 248 int 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 264 int 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 296 U_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