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