linux/drivers/net/dsa/mv88e6xxx/serdes.c
<<
>>
Prefs
   1/*
   2 * Marvell 88E6xxx SERDES manipulation, via SMI bus
   3 *
   4 * Copyright (c) 2008 Marvell Semiconductor
   5 *
   6 * Copyright (c) 2017 Andrew Lunn <andrew@lunn.ch>
   7 *
   8 * This program is free software; you can redistribute it and/or modify
   9 * it under the terms of the GNU General Public License as published by
  10 * the Free Software Foundation; either version 2 of the License, or
  11 * (at your option) any later version.
  12 */
  13
  14#include <linux/mii.h>
  15
  16#include "chip.h"
  17#include "global2.h"
  18#include "phy.h"
  19#include "port.h"
  20#include "serdes.h"
  21
  22static int mv88e6352_serdes_read(struct mv88e6xxx_chip *chip, int reg,
  23                                 u16 *val)
  24{
  25        return mv88e6xxx_phy_page_read(chip, MV88E6352_ADDR_SERDES,
  26                                       MV88E6352_SERDES_PAGE_FIBER,
  27                                       reg, val);
  28}
  29
  30static int mv88e6352_serdes_write(struct mv88e6xxx_chip *chip, int reg,
  31                                  u16 val)
  32{
  33        return mv88e6xxx_phy_page_write(chip, MV88E6352_ADDR_SERDES,
  34                                        MV88E6352_SERDES_PAGE_FIBER,
  35                                        reg, val);
  36}
  37
  38static int mv88e6352_serdes_power_set(struct mv88e6xxx_chip *chip, bool on)
  39{
  40        u16 val, new_val;
  41        int err;
  42
  43        err = mv88e6352_serdes_read(chip, MII_BMCR, &val);
  44        if (err)
  45                return err;
  46
  47        if (on)
  48                new_val = val & ~BMCR_PDOWN;
  49        else
  50                new_val = val | BMCR_PDOWN;
  51
  52        if (val != new_val)
  53                err = mv88e6352_serdes_write(chip, MII_BMCR, new_val);
  54
  55        return err;
  56}
  57
  58int mv88e6352_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on)
  59{
  60        int err;
  61        u8 cmode;
  62
  63        err = mv88e6xxx_port_get_cmode(chip, port, &cmode);
  64        if (err)
  65                return err;
  66
  67        if ((cmode == MV88E6XXX_PORT_STS_CMODE_100BASE_X) ||
  68            (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASE_X) ||
  69            (cmode == MV88E6XXX_PORT_STS_CMODE_SGMII)) {
  70                err = mv88e6352_serdes_power_set(chip, on);
  71                if (err < 0)
  72                        return err;
  73        }
  74
  75        return 0;
  76}
  77
  78/* Set the power on/off for 10GBASE-R and 10GBASE-X4/X2 */
  79static int mv88e6390_serdes_10g(struct mv88e6xxx_chip *chip, int addr, bool on)
  80{
  81        u16 val, new_val;
  82        int reg_c45;
  83        int err;
  84
  85        reg_c45 = MII_ADDR_C45 | MV88E6390_SERDES_DEVICE |
  86                MV88E6390_PCS_CONTROL_1;
  87        err = mv88e6xxx_phy_read(chip, addr, reg_c45, &val);
  88        if (err)
  89                return err;
  90
  91        if (on)
  92                new_val = val & ~(MV88E6390_PCS_CONTROL_1_RESET |
  93                                  MV88E6390_PCS_CONTROL_1_LOOPBACK |
  94                                  MV88E6390_PCS_CONTROL_1_PDOWN);
  95        else
  96                new_val = val | MV88E6390_PCS_CONTROL_1_PDOWN;
  97
  98        if (val != new_val)
  99                err = mv88e6xxx_phy_write(chip, addr, reg_c45, new_val);
 100
 101        return err;
 102}
 103
 104/* Set the power on/off for 10GBASE-R and 10GBASE-X4/X2 */
 105static int mv88e6390_serdes_sgmii(struct mv88e6xxx_chip *chip, int addr,
 106                                  bool on)
 107{
 108        u16 val, new_val;
 109        int reg_c45;
 110        int err;
 111
 112        reg_c45 = MII_ADDR_C45 | MV88E6390_SERDES_DEVICE |
 113                MV88E6390_SGMII_CONTROL;
 114        err = mv88e6xxx_phy_read(chip, addr, reg_c45, &val);
 115        if (err)
 116                return err;
 117
 118        if (on)
 119                new_val = val & ~(MV88E6390_SGMII_CONTROL_RESET |
 120                                  MV88E6390_SGMII_CONTROL_LOOPBACK |
 121                                  MV88E6390_SGMII_CONTROL_PDOWN);
 122        else
 123                new_val = val | MV88E6390_SGMII_CONTROL_PDOWN;
 124
 125        if (val != new_val)
 126                err = mv88e6xxx_phy_write(chip, addr, reg_c45, new_val);
 127
 128        return err;
 129}
 130
 131static int mv88e6390_serdes_lower(struct mv88e6xxx_chip *chip, u8 cmode,
 132                                  int port_donor, int lane, bool rxaui, bool on)
 133{
 134        int err;
 135        u8 cmode_donor;
 136
 137        err = mv88e6xxx_port_get_cmode(chip, port_donor, &cmode_donor);
 138        if (err)
 139                return err;
 140
 141        switch (cmode_donor) {
 142        case MV88E6XXX_PORT_STS_CMODE_RXAUI:
 143                if (!rxaui)
 144                        break;
 145                /* Fall through */
 146        case MV88E6XXX_PORT_STS_CMODE_1000BASE_X:
 147        case MV88E6XXX_PORT_STS_CMODE_SGMII:
 148        case MV88E6XXX_PORT_STS_CMODE_2500BASEX:
 149                if (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASE_X ||
 150                    cmode == MV88E6XXX_PORT_STS_CMODE_SGMII)
 151                        return  mv88e6390_serdes_sgmii(chip, lane, on);
 152        }
 153        return 0;
 154}
 155
 156static int mv88e6390_serdes_port9(struct mv88e6xxx_chip *chip, u8 cmode,
 157                                  bool on)
 158{
 159        switch (cmode) {
 160        case MV88E6XXX_PORT_STS_CMODE_1000BASE_X:
 161        case MV88E6XXX_PORT_STS_CMODE_SGMII:
 162                return mv88e6390_serdes_sgmii(chip, MV88E6390_PORT9_LANE0, on);
 163        case MV88E6XXX_PORT_STS_CMODE_XAUI:
 164        case MV88E6XXX_PORT_STS_CMODE_RXAUI:
 165        case MV88E6XXX_PORT_STS_CMODE_2500BASEX:
 166                return mv88e6390_serdes_10g(chip, MV88E6390_PORT9_LANE0, on);
 167        }
 168
 169        return 0;
 170}
 171
 172static int mv88e6390_serdes_port10(struct mv88e6xxx_chip *chip, u8 cmode,
 173                                   bool on)
 174{
 175        switch (cmode) {
 176        case MV88E6XXX_PORT_STS_CMODE_SGMII:
 177                return mv88e6390_serdes_sgmii(chip, MV88E6390_PORT10_LANE0, on);
 178        case MV88E6XXX_PORT_STS_CMODE_XAUI:
 179        case MV88E6XXX_PORT_STS_CMODE_RXAUI:
 180        case MV88E6XXX_PORT_STS_CMODE_1000BASE_X:
 181        case MV88E6XXX_PORT_STS_CMODE_2500BASEX:
 182                return mv88e6390_serdes_10g(chip, MV88E6390_PORT10_LANE0, on);
 183        }
 184
 185        return 0;
 186}
 187
 188int mv88e6390_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on)
 189{
 190        u8 cmode;
 191        int err;
 192
 193        err = mv88e6xxx_port_get_cmode(chip, port, &cmode);
 194        if (err)
 195                return err;
 196
 197        switch (port) {
 198        case 2:
 199                return mv88e6390_serdes_lower(chip, cmode, 9,
 200                                              MV88E6390_PORT9_LANE1,
 201                                              false, on);
 202        case 3:
 203                return mv88e6390_serdes_lower(chip, cmode, 9,
 204                                              MV88E6390_PORT9_LANE2,
 205                                              true, on);
 206        case 4:
 207                return mv88e6390_serdes_lower(chip, cmode, 9,
 208                                              MV88E6390_PORT9_LANE3,
 209                                              true, on);
 210        case 5:
 211                return mv88e6390_serdes_lower(chip, cmode, 10,
 212                                              MV88E6390_PORT10_LANE1,
 213                                              false, on);
 214        case 6:
 215                return mv88e6390_serdes_lower(chip, cmode, 10,
 216                                              MV88E6390_PORT10_LANE2,
 217                                              true, on);
 218        case 7:
 219                return mv88e6390_serdes_lower(chip, cmode, 10,
 220                                              MV88E6390_PORT10_LANE3,
 221                                              true, on);
 222        case 9:
 223                return mv88e6390_serdes_port9(chip, cmode, on);
 224        case 10:
 225                return mv88e6390_serdes_port10(chip, cmode, on);
 226        }
 227
 228        return 0;
 229}
 230