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