1*2e192b24SSimon Glass /* 2*2e192b24SSimon Glass * (C) Copyright 2011 Freescale Semiconductor, Inc 3*2e192b24SSimon Glass * Andy Fleming 4*2e192b24SSimon Glass * 5*2e192b24SSimon Glass * SPDX-License-Identifier: GPL-2.0+ 6*2e192b24SSimon Glass */ 7*2e192b24SSimon Glass 8*2e192b24SSimon Glass /* 9*2e192b24SSimon Glass * MDIO Commands 10*2e192b24SSimon Glass */ 11*2e192b24SSimon Glass 12*2e192b24SSimon Glass #include <common.h> 13*2e192b24SSimon Glass #include <command.h> 14*2e192b24SSimon Glass #include <miiphy.h> 15*2e192b24SSimon Glass #include <phy.h> 16*2e192b24SSimon Glass 17*2e192b24SSimon Glass 18*2e192b24SSimon Glass static char last_op[2]; 19*2e192b24SSimon Glass static uint last_data; 20*2e192b24SSimon Glass static uint last_addr_lo; 21*2e192b24SSimon Glass static uint last_addr_hi; 22*2e192b24SSimon Glass static uint last_devad_lo; 23*2e192b24SSimon Glass static uint last_devad_hi; 24*2e192b24SSimon Glass static uint last_reg_lo; 25*2e192b24SSimon Glass static uint last_reg_hi; 26*2e192b24SSimon Glass 27*2e192b24SSimon Glass static int extract_range(char *input, int *plo, int *phi) 28*2e192b24SSimon Glass { 29*2e192b24SSimon Glass char *end; 30*2e192b24SSimon Glass *plo = simple_strtol(input, &end, 0); 31*2e192b24SSimon Glass if (end == input) 32*2e192b24SSimon Glass return -1; 33*2e192b24SSimon Glass 34*2e192b24SSimon Glass if ((*end == '-') && *(++end)) 35*2e192b24SSimon Glass *phi = simple_strtol(end, NULL, 0); 36*2e192b24SSimon Glass else if (*end == '\0') 37*2e192b24SSimon Glass *phi = *plo; 38*2e192b24SSimon Glass else 39*2e192b24SSimon Glass return -1; 40*2e192b24SSimon Glass 41*2e192b24SSimon Glass return 0; 42*2e192b24SSimon Glass } 43*2e192b24SSimon Glass 44*2e192b24SSimon Glass static int mdio_write_ranges(struct phy_device *phydev, struct mii_dev *bus, 45*2e192b24SSimon Glass int addrlo, 46*2e192b24SSimon Glass int addrhi, int devadlo, int devadhi, 47*2e192b24SSimon Glass int reglo, int reghi, unsigned short data, 48*2e192b24SSimon Glass int extended) 49*2e192b24SSimon Glass { 50*2e192b24SSimon Glass int addr, devad, reg; 51*2e192b24SSimon Glass int err = 0; 52*2e192b24SSimon Glass 53*2e192b24SSimon Glass for (addr = addrlo; addr <= addrhi; addr++) { 54*2e192b24SSimon Glass for (devad = devadlo; devad <= devadhi; devad++) { 55*2e192b24SSimon Glass for (reg = reglo; reg <= reghi; reg++) { 56*2e192b24SSimon Glass if (!extended) 57*2e192b24SSimon Glass err = bus->write(bus, addr, devad, 58*2e192b24SSimon Glass reg, data); 59*2e192b24SSimon Glass else 60*2e192b24SSimon Glass err = phydev->drv->writeext(phydev, 61*2e192b24SSimon Glass addr, devad, reg, data); 62*2e192b24SSimon Glass 63*2e192b24SSimon Glass if (err) 64*2e192b24SSimon Glass goto err_out; 65*2e192b24SSimon Glass } 66*2e192b24SSimon Glass } 67*2e192b24SSimon Glass } 68*2e192b24SSimon Glass 69*2e192b24SSimon Glass err_out: 70*2e192b24SSimon Glass return err; 71*2e192b24SSimon Glass } 72*2e192b24SSimon Glass 73*2e192b24SSimon Glass static int mdio_read_ranges(struct phy_device *phydev, struct mii_dev *bus, 74*2e192b24SSimon Glass int addrlo, 75*2e192b24SSimon Glass int addrhi, int devadlo, int devadhi, 76*2e192b24SSimon Glass int reglo, int reghi, int extended) 77*2e192b24SSimon Glass { 78*2e192b24SSimon Glass int addr, devad, reg; 79*2e192b24SSimon Glass 80*2e192b24SSimon Glass printf("Reading from bus %s\n", bus->name); 81*2e192b24SSimon Glass for (addr = addrlo; addr <= addrhi; addr++) { 82*2e192b24SSimon Glass printf("PHY at address %d:\n", addr); 83*2e192b24SSimon Glass 84*2e192b24SSimon Glass for (devad = devadlo; devad <= devadhi; devad++) { 85*2e192b24SSimon Glass for (reg = reglo; reg <= reghi; reg++) { 86*2e192b24SSimon Glass int val; 87*2e192b24SSimon Glass 88*2e192b24SSimon Glass if (!extended) 89*2e192b24SSimon Glass val = bus->read(bus, addr, devad, reg); 90*2e192b24SSimon Glass else 91*2e192b24SSimon Glass val = phydev->drv->readext(phydev, addr, 92*2e192b24SSimon Glass devad, reg); 93*2e192b24SSimon Glass 94*2e192b24SSimon Glass if (val < 0) { 95*2e192b24SSimon Glass printf("Error\n"); 96*2e192b24SSimon Glass 97*2e192b24SSimon Glass return val; 98*2e192b24SSimon Glass } 99*2e192b24SSimon Glass 100*2e192b24SSimon Glass if (devad >= 0) 101*2e192b24SSimon Glass printf("%d.", devad); 102*2e192b24SSimon Glass 103*2e192b24SSimon Glass printf("%d - 0x%x\n", reg, val & 0xffff); 104*2e192b24SSimon Glass } 105*2e192b24SSimon Glass } 106*2e192b24SSimon Glass } 107*2e192b24SSimon Glass 108*2e192b24SSimon Glass return 0; 109*2e192b24SSimon Glass } 110*2e192b24SSimon Glass 111*2e192b24SSimon Glass /* The register will be in the form [a[-b].]x[-y] */ 112*2e192b24SSimon Glass static int extract_reg_range(char *input, int *devadlo, int *devadhi, 113*2e192b24SSimon Glass int *reglo, int *reghi) 114*2e192b24SSimon Glass { 115*2e192b24SSimon Glass char *regstr; 116*2e192b24SSimon Glass 117*2e192b24SSimon Glass /* use strrchr to find the last string after a '.' */ 118*2e192b24SSimon Glass regstr = strrchr(input, '.'); 119*2e192b24SSimon Glass 120*2e192b24SSimon Glass /* If it exists, extract the devad(s) */ 121*2e192b24SSimon Glass if (regstr) { 122*2e192b24SSimon Glass char devadstr[32]; 123*2e192b24SSimon Glass 124*2e192b24SSimon Glass strncpy(devadstr, input, regstr - input); 125*2e192b24SSimon Glass devadstr[regstr - input] = '\0'; 126*2e192b24SSimon Glass 127*2e192b24SSimon Glass if (extract_range(devadstr, devadlo, devadhi)) 128*2e192b24SSimon Glass return -1; 129*2e192b24SSimon Glass 130*2e192b24SSimon Glass regstr++; 131*2e192b24SSimon Glass } else { 132*2e192b24SSimon Glass /* Otherwise, we have no devad, and we just got regs */ 133*2e192b24SSimon Glass *devadlo = *devadhi = MDIO_DEVAD_NONE; 134*2e192b24SSimon Glass 135*2e192b24SSimon Glass regstr = input; 136*2e192b24SSimon Glass } 137*2e192b24SSimon Glass 138*2e192b24SSimon Glass return extract_range(regstr, reglo, reghi); 139*2e192b24SSimon Glass } 140*2e192b24SSimon Glass 141*2e192b24SSimon Glass static int extract_phy_range(char *const argv[], int argc, struct mii_dev **bus, 142*2e192b24SSimon Glass struct phy_device **phydev, 143*2e192b24SSimon Glass int *addrlo, int *addrhi) 144*2e192b24SSimon Glass { 145*2e192b24SSimon Glass struct phy_device *dev = *phydev; 146*2e192b24SSimon Glass 147*2e192b24SSimon Glass if ((argc < 1) || (argc > 2)) 148*2e192b24SSimon Glass return -1; 149*2e192b24SSimon Glass 150*2e192b24SSimon Glass /* If there are two arguments, it's busname addr */ 151*2e192b24SSimon Glass if (argc == 2) { 152*2e192b24SSimon Glass *bus = miiphy_get_dev_by_name(argv[0]); 153*2e192b24SSimon Glass 154*2e192b24SSimon Glass if (!*bus) 155*2e192b24SSimon Glass return -1; 156*2e192b24SSimon Glass 157*2e192b24SSimon Glass return extract_range(argv[1], addrlo, addrhi); 158*2e192b24SSimon Glass } 159*2e192b24SSimon Glass 160*2e192b24SSimon Glass /* It must be one argument, here */ 161*2e192b24SSimon Glass 162*2e192b24SSimon Glass /* 163*2e192b24SSimon Glass * This argument can be one of two things: 164*2e192b24SSimon Glass * 1) Ethernet device name 165*2e192b24SSimon Glass * 2) Just an address (use the previously-used bus) 166*2e192b24SSimon Glass * 167*2e192b24SSimon Glass * We check all buses for a PHY which is connected to an ethernet 168*2e192b24SSimon Glass * device by the given name. If none are found, we call 169*2e192b24SSimon Glass * extract_range() on the string, and see if it's an address range. 170*2e192b24SSimon Glass */ 171*2e192b24SSimon Glass dev = mdio_phydev_for_ethname(argv[0]); 172*2e192b24SSimon Glass 173*2e192b24SSimon Glass if (dev) { 174*2e192b24SSimon Glass *addrlo = *addrhi = dev->addr; 175*2e192b24SSimon Glass *bus = dev->bus; 176*2e192b24SSimon Glass 177*2e192b24SSimon Glass return 0; 178*2e192b24SSimon Glass } 179*2e192b24SSimon Glass 180*2e192b24SSimon Glass /* It's an address or nothing useful */ 181*2e192b24SSimon Glass return extract_range(argv[0], addrlo, addrhi); 182*2e192b24SSimon Glass } 183*2e192b24SSimon Glass 184*2e192b24SSimon Glass /* ---------------------------------------------------------------- */ 185*2e192b24SSimon Glass static int do_mdio(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) 186*2e192b24SSimon Glass { 187*2e192b24SSimon Glass char op[2]; 188*2e192b24SSimon Glass int addrlo, addrhi, reglo, reghi, devadlo, devadhi; 189*2e192b24SSimon Glass unsigned short data; 190*2e192b24SSimon Glass int pos = argc - 1; 191*2e192b24SSimon Glass struct mii_dev *bus; 192*2e192b24SSimon Glass struct phy_device *phydev = NULL; 193*2e192b24SSimon Glass int extended = 0; 194*2e192b24SSimon Glass 195*2e192b24SSimon Glass if (argc < 2) 196*2e192b24SSimon Glass return CMD_RET_USAGE; 197*2e192b24SSimon Glass 198*2e192b24SSimon Glass /* 199*2e192b24SSimon Glass * We use the last specified parameters, unless new ones are 200*2e192b24SSimon Glass * entered. 201*2e192b24SSimon Glass */ 202*2e192b24SSimon Glass op[0] = argv[1][0]; 203*2e192b24SSimon Glass addrlo = last_addr_lo; 204*2e192b24SSimon Glass addrhi = last_addr_hi; 205*2e192b24SSimon Glass devadlo = last_devad_lo; 206*2e192b24SSimon Glass devadhi = last_devad_hi; 207*2e192b24SSimon Glass reglo = last_reg_lo; 208*2e192b24SSimon Glass reghi = last_reg_hi; 209*2e192b24SSimon Glass data = last_data; 210*2e192b24SSimon Glass 211*2e192b24SSimon Glass bus = mdio_get_current_dev(); 212*2e192b24SSimon Glass 213*2e192b24SSimon Glass if (flag & CMD_FLAG_REPEAT) 214*2e192b24SSimon Glass op[0] = last_op[0]; 215*2e192b24SSimon Glass 216*2e192b24SSimon Glass if (strlen(argv[1]) > 1) { 217*2e192b24SSimon Glass op[1] = argv[1][1]; 218*2e192b24SSimon Glass if (op[1] == 'x') { 219*2e192b24SSimon Glass phydev = mdio_phydev_for_ethname(argv[2]); 220*2e192b24SSimon Glass 221*2e192b24SSimon Glass if (phydev) { 222*2e192b24SSimon Glass addrlo = phydev->addr; 223*2e192b24SSimon Glass addrhi = addrlo; 224*2e192b24SSimon Glass bus = phydev->bus; 225*2e192b24SSimon Glass extended = 1; 226*2e192b24SSimon Glass } else { 227*2e192b24SSimon Glass return -1; 228*2e192b24SSimon Glass } 229*2e192b24SSimon Glass 230*2e192b24SSimon Glass if (!phydev->drv || 231*2e192b24SSimon Glass (!phydev->drv->writeext && (op[0] == 'w')) || 232*2e192b24SSimon Glass (!phydev->drv->readext && (op[0] == 'r'))) { 233*2e192b24SSimon Glass puts("PHY does not have extended functions\n"); 234*2e192b24SSimon Glass return -1; 235*2e192b24SSimon Glass } 236*2e192b24SSimon Glass } 237*2e192b24SSimon Glass } 238*2e192b24SSimon Glass 239*2e192b24SSimon Glass switch (op[0]) { 240*2e192b24SSimon Glass case 'w': 241*2e192b24SSimon Glass if (pos > 1) 242*2e192b24SSimon Glass data = simple_strtoul(argv[pos--], NULL, 16); 243*2e192b24SSimon Glass case 'r': 244*2e192b24SSimon Glass if (pos > 1) 245*2e192b24SSimon Glass if (extract_reg_range(argv[pos--], &devadlo, &devadhi, 246*2e192b24SSimon Glass ®lo, ®hi)) 247*2e192b24SSimon Glass return -1; 248*2e192b24SSimon Glass 249*2e192b24SSimon Glass default: 250*2e192b24SSimon Glass if (pos > 1) 251*2e192b24SSimon Glass if (extract_phy_range(&(argv[2]), pos - 1, &bus, 252*2e192b24SSimon Glass &phydev, &addrlo, &addrhi)) 253*2e192b24SSimon Glass return -1; 254*2e192b24SSimon Glass 255*2e192b24SSimon Glass break; 256*2e192b24SSimon Glass } 257*2e192b24SSimon Glass 258*2e192b24SSimon Glass if (op[0] == 'l') { 259*2e192b24SSimon Glass mdio_list_devices(); 260*2e192b24SSimon Glass 261*2e192b24SSimon Glass return 0; 262*2e192b24SSimon Glass } 263*2e192b24SSimon Glass 264*2e192b24SSimon Glass /* Save the chosen bus */ 265*2e192b24SSimon Glass miiphy_set_current_dev(bus->name); 266*2e192b24SSimon Glass 267*2e192b24SSimon Glass switch (op[0]) { 268*2e192b24SSimon Glass case 'w': 269*2e192b24SSimon Glass mdio_write_ranges(phydev, bus, addrlo, addrhi, devadlo, devadhi, 270*2e192b24SSimon Glass reglo, reghi, data, extended); 271*2e192b24SSimon Glass break; 272*2e192b24SSimon Glass 273*2e192b24SSimon Glass case 'r': 274*2e192b24SSimon Glass mdio_read_ranges(phydev, bus, addrlo, addrhi, devadlo, devadhi, 275*2e192b24SSimon Glass reglo, reghi, extended); 276*2e192b24SSimon Glass break; 277*2e192b24SSimon Glass } 278*2e192b24SSimon Glass 279*2e192b24SSimon Glass /* 280*2e192b24SSimon Glass * Save the parameters for repeats. 281*2e192b24SSimon Glass */ 282*2e192b24SSimon Glass last_op[0] = op[0]; 283*2e192b24SSimon Glass last_addr_lo = addrlo; 284*2e192b24SSimon Glass last_addr_hi = addrhi; 285*2e192b24SSimon Glass last_devad_lo = devadlo; 286*2e192b24SSimon Glass last_devad_hi = devadhi; 287*2e192b24SSimon Glass last_reg_lo = reglo; 288*2e192b24SSimon Glass last_reg_hi = reghi; 289*2e192b24SSimon Glass last_data = data; 290*2e192b24SSimon Glass 291*2e192b24SSimon Glass return 0; 292*2e192b24SSimon Glass } 293*2e192b24SSimon Glass 294*2e192b24SSimon Glass /***************************************************/ 295*2e192b24SSimon Glass 296*2e192b24SSimon Glass U_BOOT_CMD( 297*2e192b24SSimon Glass mdio, 6, 1, do_mdio, 298*2e192b24SSimon Glass "MDIO utility commands", 299*2e192b24SSimon Glass "list - List MDIO buses\n" 300*2e192b24SSimon Glass "mdio read <phydev> [<devad>.]<reg> - " 301*2e192b24SSimon Glass "read PHY's register at <devad>.<reg>\n" 302*2e192b24SSimon Glass "mdio write <phydev> [<devad>.]<reg> <data> - " 303*2e192b24SSimon Glass "write PHY's register at <devad>.<reg>\n" 304*2e192b24SSimon Glass "mdio rx <phydev> [<devad>.]<reg> - " 305*2e192b24SSimon Glass "read PHY's extended register at <devad>.<reg>\n" 306*2e192b24SSimon Glass "mdio wx <phydev> [<devad>.]<reg> <data> - " 307*2e192b24SSimon Glass "write PHY's extended register at <devad>.<reg>\n" 308*2e192b24SSimon Glass "<phydev> may be:\n" 309*2e192b24SSimon Glass " <busname> <addr>\n" 310*2e192b24SSimon Glass " <addr>\n" 311*2e192b24SSimon Glass " <eth name>\n" 312*2e192b24SSimon Glass "<addr> <devad>, and <reg> may be ranges, e.g. 1-5.4-0x1f.\n" 313*2e192b24SSimon Glass ); 314