1/* ********************************************************************* 2 * Broadcom Common Firmware Environment (CFE) 3 * 4 * PHY hacking commands File: ui_phycmds.c 5 * 6 * These commands let you directly muck with the PHYs 7 * attached to the Ethernet controllers. 8 * 9 * Author: Mitch Lichtenberg (mpl@broadcom.com) 10 * 11 ********************************************************************* 12 * 13 * Copyright 2000,2001,2002,2003 14 * Broadcom Corporation. All rights reserved. 15 * 16 * This software is furnished under license and may be used and 17 * copied only in accordance with the following terms and 18 * conditions. Subject to these conditions, you may download, 19 * copy, install, use, modify and distribute modified or unmodified 20 * copies of this software in source and/or binary form. No title 21 * or ownership is transferred hereby. 22 * 23 * 1) Any source code used, modified or distributed must reproduce 24 * and retain this copyright notice and list of conditions 25 * as they appear in the source file. 26 * 27 * 2) No right is granted to use any trade name, trademark, or 28 * logo of Broadcom Corporation. The "Broadcom Corporation" 29 * name may not be used to endorse or promote products derived 30 * from this software without the prior written permission of 31 * Broadcom Corporation. 32 * 33 * 3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR 34 * IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED 35 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 36 * PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT 37 * SHALL BROADCOM BE LIABLE FOR ANY DAMAGES WHATSOEVER, AND IN 38 * PARTICULAR, BROADCOM SHALL NOT BE LIABLE FOR DIRECT, INDIRECT, 39 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 40 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 41 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 42 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 43 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 44 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE), EVEN IF ADVISED OF 45 * THE POSSIBILITY OF SUCH DAMAGE. 46 ********************************************************************* */ 47 48 49 50 51#include "sbmips.h" 52 53#include "lib_types.h" 54#include "lib_string.h" 55#include "lib_queue.h" 56#include "lib_malloc.h" 57#include "lib_printf.h" 58#include "lib_arena.h" 59 60#include "ui_command.h" 61 62#include "sb1250_defs.h" 63#include "sb1250_regs.h" 64#include "sb1250_mac.h" 65 66#include "cfe.h" 67#include "cfe_error.h" 68#include "mii.h" 69 70 71#define PHY_READCSR(t) (*((volatile uint64_t *) (t))) 72#define PHY_WRITECSR(t,v) *((volatile uint64_t *) (t)) = (v) 73 74#define M_MAC_MDIO_DIR_OUTPUT 0 /* for clarity */ 75 76#ifdef __long64 77typedef volatile uint64_t phy_port_t; 78typedef uint64_t phy_physaddr_t; 79#define PHY_PORT(x) PHYS_TO_K1(x) 80#else 81typedef volatile uint32_t phy_port_t; 82typedef uint32_t phy_physaddr_t; 83#define PHY_PORT(x) PHYS_TO_K1(x) 84#endif 85 86typedef struct phy_s { 87 phy_port_t sbe_mdio; 88} phy_t; 89 90 91int ui_init_phycmds(void); 92 93static int ui_cmd_phydump(ui_cmdline_t *cmd,int argc,char *argv[]); 94static int ui_cmd_physet(ui_cmdline_t *cmd,int argc,char *argv[]); 95 96static void phy_mii_write(phy_t *s,int phyaddr,int regidx, 97 unsigned int regval); 98static unsigned int phy_mii_read(phy_t *s,int phyaddr,int regidx); 99 100 101int ui_init_phycmds(void) 102{ 103 cmd_addcmd("phy dump", 104 ui_cmd_phydump, 105 NULL, 106 "Dump the registers on the PHY", 107 "phy dump macid [reg]\n\n" 108 "This command displays the contents of the registers on the PHY\n" 109 "attached to the specified Ethernet controller. macid is the\n" 110 "Ethernet controller ID (0..2 for the BCM1250) and reg\n" 111 "is an optional register number (0..31). By default, all registers\n" 112 "are displayed.", 113 "-phy=*;Specify PHY address (default=1)"); 114 115 cmd_addcmd("phy set", 116 ui_cmd_physet, 117 NULL, 118 "Set the value of a PHY register", 119 "phy set macid reg value\n\n" 120 "Sets the value of a register on a PHY. macid is the Ethernet\n" 121 "controller number (0..2 for the BCM1250), reg is the register\n" 122 "number (0..31), and value is the 16-bit value to write to the\n" 123 "register.\n", 124 "-phy=*;Specify PHY address (default=1)"); 125 126 return 0; 127} 128 129static int ui_cmd_physet(ui_cmdline_t *cmd,int argc,char *argv[]) 130{ 131 phy_t phy; 132 int phynum; 133 int mac; 134 char *x; 135 unsigned int value; 136 unsigned int reg; 137 138 x = cmd_getarg(cmd,0); 139 if (!x) return ui_showusage(cmd); 140 141 mac = atoi(x); 142 if ((mac < 0) || (mac > 2)) { 143 return ui_showerror(CFE_ERR_INV_PARAM,"Invalid MAC number"); 144 } 145 phy.sbe_mdio = PHY_PORT(A_MAC_REGISTER(mac,R_MAC_MDIO)); 146 147 if (cmd_sw_value(cmd,"-phy",&x)) { 148 phynum = atoi(x); 149 } 150 else phynum = 1; 151 152 x = cmd_getarg(cmd,1); 153 if (!x) return ui_showusage(cmd); 154 reg = atoi(x); 155 if ((reg < 0) || (reg > 31)) { 156 return ui_showerror(CFE_ERR_INV_PARAM,"Invalid phy register number"); 157 } 158 159 x = cmd_getarg(cmd,2); 160 if (!x) return ui_showusage(cmd); 161 value = atoi(x) & 0xFFFF; 162 163 phy_mii_write(&phy,phynum,reg,value); 164 165 printf("Wrote 0x%04X to phy %d register 0x%02X on mac %d\n", 166 value,phynum,reg,mac); 167 168 return 0; 169} 170 171static int ui_cmd_phydump(ui_cmdline_t *cmd,int argc,char *argv[]) 172{ 173 phy_t phy; 174 int phynum; 175 int idx; 176 int mac; 177 char *x; 178 unsigned int reg; 179 int allreg = 1; 180 181 x = cmd_getarg(cmd,0); 182 if (!x) return ui_showusage(cmd); 183 184 mac = atoi(x); 185 if ((mac < 0) || (mac > 2)) { 186 return ui_showerror(CFE_ERR_INV_PARAM,"Invalid MAC number"); 187 } 188 phy.sbe_mdio = PHY_PORT(A_MAC_REGISTER(mac,R_MAC_MDIO)); 189 190 if (cmd_sw_value(cmd,"-phy",&x)) { 191 phynum = atoi(x); 192 } 193 else phynum = 1; 194 195 x = cmd_getarg(cmd,1); 196 reg = 0; 197 if (x) { 198 reg = atoi(x); 199 if ((reg < 0) || (reg > 31)) { 200 return ui_showerror(CFE_ERR_INV_PARAM,"Invalid phy register number"); 201 } 202 allreg = 0; 203 } 204 205 if (allreg) { 206 printf("** PHY registers on MAC %d PHY %d **\n",mac,phynum); 207 for (idx = 0; idx < 31; idx+=2) { 208 printf("Reg 0x%02X = 0x%04X | ",idx,phy_mii_read(&phy,phynum,idx)); 209 printf("Reg 0x%02X = 0x%04X",idx+1,phy_mii_read(&phy,phynum,idx+1)); 210 printf("\n"); 211 } 212 } 213 else { 214 printf("Reg %02X = %04X\n",reg,phy_mii_read(&phy,phynum,reg)); 215 } 216 217 return 0; 218 219} 220 221 222 223 224/* ********************************************************************* 225 * PHY_MII_SYNC(s) 226 * 227 * Synchronize with the MII - send a pattern of bits to the MII 228 * that will guarantee that it is ready to accept a command. 229 * 230 * Input parameters: 231 * s - sbmac structure 232 * 233 * Return value: 234 * nothing 235 ********************************************************************* */ 236 237static void phy_mii_sync(phy_t *s) 238{ 239 int cnt; 240 uint64_t bits; 241 int mac_mdio_genc; /*genc bit needs to be saved*/ 242 243 mac_mdio_genc = PHY_READCSR(s->sbe_mdio) & M_MAC_GENC; 244 245 bits = M_MAC_MDIO_DIR_OUTPUT | M_MAC_MDIO_OUT; 246 247 PHY_WRITECSR(s->sbe_mdio,bits | mac_mdio_genc); 248 249 for (cnt = 0; cnt < 32; cnt++) { 250 PHY_WRITECSR(s->sbe_mdio,bits | M_MAC_MDC | mac_mdio_genc); 251 PHY_WRITECSR(s->sbe_mdio,bits | mac_mdio_genc); 252 } 253 254} 255 256/* ********************************************************************* 257 * PHY_MII_SENDDATA(s,data,bitcnt) 258 * 259 * Send some bits to the MII. The bits to be sent are right- 260 * justified in the 'data' parameter. 261 * 262 * Input parameters: 263 * s - sbmac structure 264 * data - data to send 265 * bitcnt - number of bits to send 266 ********************************************************************* */ 267 268static void phy_mii_senddata(phy_t *s,unsigned int data, int bitcnt) 269{ 270 int i; 271 uint64_t bits; 272 unsigned int curmask; 273 int mac_mdio_genc; 274 275 mac_mdio_genc = PHY_READCSR(s->sbe_mdio) & M_MAC_GENC; 276 277 bits = M_MAC_MDIO_DIR_OUTPUT; 278 PHY_WRITECSR(s->sbe_mdio,bits | mac_mdio_genc); 279 280 curmask = 1 << (bitcnt - 1); 281 282 for (i = 0; i < bitcnt; i++) { 283 if (data & curmask) bits |= M_MAC_MDIO_OUT; 284 else bits &= ~M_MAC_MDIO_OUT; 285 PHY_WRITECSR(s->sbe_mdio,bits | mac_mdio_genc); 286 PHY_WRITECSR(s->sbe_mdio,bits | M_MAC_MDC | mac_mdio_genc); 287 PHY_WRITECSR(s->sbe_mdio,bits | mac_mdio_genc); 288 curmask >>= 1; 289 } 290} 291 292 293 294/* ********************************************************************* 295 * PHY_MII_READ(s,phyaddr,regidx) 296 * 297 * Read a PHY register. 298 * 299 * Input parameters: 300 * s - sbmac structure 301 * phyaddr - PHY's address 302 * regidx = index of register to read 303 * 304 * Return value: 305 * value read, or 0 if an error occured. 306 ********************************************************************* */ 307 308static unsigned int phy_mii_read(phy_t *s,int phyaddr,int regidx) 309{ 310 int idx; 311 int error; 312 int regval; 313 int mac_mdio_genc; 314 315 /* 316 * Synchronize ourselves so that the PHY knows the next 317 * thing coming down is a command 318 */ 319 320 phy_mii_sync(s); 321 322 /* 323 * Send the data to the PHY. The sequence is 324 * a "start" command (2 bits) 325 * a "read" command (2 bits) 326 * the PHY addr (5 bits) 327 * the register index (5 bits) 328 */ 329 330 phy_mii_senddata(s,MII_COMMAND_START, 2); 331 phy_mii_senddata(s,MII_COMMAND_READ, 2); 332 phy_mii_senddata(s,phyaddr, 5); 333 phy_mii_senddata(s,regidx, 5); 334 335 mac_mdio_genc = PHY_READCSR(s->sbe_mdio) & M_MAC_GENC; 336 337 /* 338 * Switch the port around without a clock transition. 339 */ 340 PHY_WRITECSR(s->sbe_mdio,M_MAC_MDIO_DIR_INPUT | mac_mdio_genc); 341 342 /* 343 * Send out a clock pulse to signal we want the status 344 */ 345 346 PHY_WRITECSR(s->sbe_mdio,M_MAC_MDIO_DIR_INPUT | M_MAC_MDC | mac_mdio_genc); 347 PHY_WRITECSR(s->sbe_mdio,M_MAC_MDIO_DIR_INPUT | mac_mdio_genc); 348 349 /* 350 * If an error occured, the PHY will signal '1' back 351 */ 352 error = PHY_READCSR(s->sbe_mdio) & M_MAC_MDIO_IN; 353 354 /* 355 * Issue an 'idle' clock pulse, but keep the direction 356 * the same. 357 */ 358 PHY_WRITECSR(s->sbe_mdio,M_MAC_MDIO_DIR_INPUT | M_MAC_MDC | mac_mdio_genc); 359 PHY_WRITECSR(s->sbe_mdio,M_MAC_MDIO_DIR_INPUT | mac_mdio_genc); 360 361 regval = 0; 362 363 for (idx = 0; idx < 16; idx++) { 364 regval <<= 1; 365 366 if (error == 0) { 367 if (PHY_READCSR(s->sbe_mdio) & M_MAC_MDIO_IN) regval |= 1; 368 } 369 370 PHY_WRITECSR(s->sbe_mdio,M_MAC_MDIO_DIR_INPUT | M_MAC_MDC | mac_mdio_genc); 371 PHY_WRITECSR(s->sbe_mdio,M_MAC_MDIO_DIR_INPUT | mac_mdio_genc); 372 } 373 374 /* Switch back to output */ 375 PHY_WRITECSR(s->sbe_mdio,M_MAC_MDIO_DIR_OUTPUT | mac_mdio_genc); 376 377 if (error == 0) return regval; 378 return 0; 379} 380 381 382/* ********************************************************************* 383 * PHY_MII_WRITE(s,phyaddr,regidx,regval) 384 * 385 * Write a value to a PHY register. 386 * 387 * Input parameters: 388 * s - sbmac structure 389 * phyaddr - PHY to use 390 * regidx - register within the PHY 391 * regval - data to write to register 392 * 393 * Return value: 394 * nothing 395 ********************************************************************* */ 396 397static void phy_mii_write(phy_t *s,int phyaddr,int regidx, 398 unsigned int regval) 399{ 400 int mac_mdio_genc; 401 402 phy_mii_sync(s); 403 404 phy_mii_senddata(s,MII_COMMAND_START,2); 405 phy_mii_senddata(s,MII_COMMAND_WRITE,2); 406 phy_mii_senddata(s,phyaddr, 5); 407 phy_mii_senddata(s,regidx, 5); 408 phy_mii_senddata(s,MII_COMMAND_ACK,2); 409 phy_mii_senddata(s,regval,16); 410 411 mac_mdio_genc = PHY_READCSR(s->sbe_mdio) & M_MAC_GENC; 412 413 PHY_WRITECSR(s->sbe_mdio,M_MAC_MDIO_DIR_OUTPUT | mac_mdio_genc); 414} 415