Skip to content

Commit

Permalink
dsa: mv88e6xxx: make serdes SGMII/Fiber tx amplitude configurable
Browse files Browse the repository at this point in the history
The mv88e6352, mv88e6240 and mv88e6176  have a serdes interface. This patch
allows to configure the output swing to a desired value in the
phy-handle of the port. The value which is peak to peak has to be
specified in microvolts. As the chips only supports eight dedicated
values we return EINVAL if the value in the DTS does not match one of
these values.

Signed-off-by: Holger Brunck <holger.brunck@hitachienergy.com>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Reviewed-by: Marek Behún <kabel@kernel.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Holger Brunck authored and David S. Miller committed Feb 11, 2022
1 parent 066c4b6 commit 926eae6
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 0 deletions.
23 changes: 23 additions & 0 deletions drivers/net/dsa/mv88e6xxx/chip.c
Original file line number Diff line number Diff line change
Expand Up @@ -2987,7 +2987,10 @@ static int mv88e6xxx_setup_upstream_port(struct mv88e6xxx_chip *chip, int port)

static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port)
{
struct device_node *phy_handle = NULL;
struct dsa_switch *ds = chip->ds;
struct dsa_port *dp;
int tx_amp;
int err;
u16 reg;

Expand Down Expand Up @@ -3178,6 +3181,23 @@ static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port)
return err;
}

if (chip->info->ops->serdes_set_tx_amplitude) {
dp = dsa_to_port(ds, port);
if (dp)
phy_handle = of_parse_phandle(dp->dn, "phy-handle", 0);

if (phy_handle && !of_property_read_u32(phy_handle,
"tx-p2p-microvolt",
&tx_amp))
err = chip->info->ops->serdes_set_tx_amplitude(chip,
port, tx_amp);
if (phy_handle) {
of_node_put(phy_handle);
if (err)
return err;
}
}

/* Port based VLAN map: give each port the same default address
* database, and allow bidirectional communication between the
* CPU and DSA port(s), and the other ports.
Expand Down Expand Up @@ -4246,6 +4266,7 @@ static const struct mv88e6xxx_ops mv88e6176_ops = {
.serdes_irq_status = mv88e6352_serdes_irq_status,
.serdes_get_regs_len = mv88e6352_serdes_get_regs_len,
.serdes_get_regs = mv88e6352_serdes_get_regs,
.serdes_set_tx_amplitude = mv88e6352_serdes_set_tx_amplitude,
.gpio_ops = &mv88e6352_gpio_ops,
.phylink_get_caps = mv88e6352_phylink_get_caps,
};
Expand Down Expand Up @@ -4526,6 +4547,7 @@ static const struct mv88e6xxx_ops mv88e6240_ops = {
.serdes_irq_status = mv88e6352_serdes_irq_status,
.serdes_get_regs_len = mv88e6352_serdes_get_regs_len,
.serdes_get_regs = mv88e6352_serdes_get_regs,
.serdes_set_tx_amplitude = mv88e6352_serdes_set_tx_amplitude,
.gpio_ops = &mv88e6352_gpio_ops,
.avb_ops = &mv88e6352_avb_ops,
.ptp_ops = &mv88e6352_ptp_ops,
Expand Down Expand Up @@ -4932,6 +4954,7 @@ static const struct mv88e6xxx_ops mv88e6352_ops = {
.serdes_get_stats = mv88e6352_serdes_get_stats,
.serdes_get_regs_len = mv88e6352_serdes_get_regs_len,
.serdes_get_regs = mv88e6352_serdes_get_regs,
.serdes_set_tx_amplitude = mv88e6352_serdes_set_tx_amplitude,
.phylink_get_caps = mv88e6352_phylink_get_caps,
};

Expand Down
4 changes: 4 additions & 0 deletions drivers/net/dsa/mv88e6xxx/chip.h
Original file line number Diff line number Diff line change
Expand Up @@ -588,6 +588,10 @@ struct mv88e6xxx_ops {
void (*serdes_get_regs)(struct mv88e6xxx_chip *chip, int port,
void *_p);

/* SERDES SGMII/Fiber Output Amplitude */
int (*serdes_set_tx_amplitude)(struct mv88e6xxx_chip *chip, int port,
int val);

/* Address Translation Unit operations */
int (*atu_get_hash)(struct mv88e6xxx_chip *chip, u8 *hash);
int (*atu_set_hash)(struct mv88e6xxx_chip *chip, u8 hash);
Expand Down
38 changes: 38 additions & 0 deletions drivers/net/dsa/mv88e6xxx/serdes.c
Original file line number Diff line number Diff line change
Expand Up @@ -1313,6 +1313,44 @@ void mv88e6390_serdes_get_regs(struct mv88e6xxx_chip *chip, int port, void *_p)
}
}

static const int mv88e6352_serdes_p2p_to_reg[] = {
/* Index of value in microvolts corresponds to the register value */
14000, 112000, 210000, 308000, 406000, 504000, 602000, 700000,
};

int mv88e6352_serdes_set_tx_amplitude(struct mv88e6xxx_chip *chip, int port,
int val)
{
bool found = false;
u16 ctrl, reg;
int err;
int i;

err = mv88e6352_g2_scratch_port_has_serdes(chip, port);
if (err <= 0)
return err;

for (i = 0; i < ARRAY_SIZE(mv88e6352_serdes_p2p_to_reg); ++i) {
if (mv88e6352_serdes_p2p_to_reg[i] == val) {
reg = i;
found = true;
break;
}
}

if (!found)
return -EINVAL;

err = mv88e6352_serdes_read(chip, MV88E6352_SERDES_SPEC_CTRL2, &ctrl);
if (err)
return err;

ctrl &= ~MV88E6352_SERDES_OUT_AMP_MASK;
ctrl |= reg;

return mv88e6352_serdes_write(chip, MV88E6352_SERDES_SPEC_CTRL2, ctrl);
}

static int mv88e6393x_serdes_power_lane(struct mv88e6xxx_chip *chip, int lane,
bool on)
{
Expand Down
5 changes: 5 additions & 0 deletions drivers/net/dsa/mv88e6xxx/serdes.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
#define MV88E6352_SERDES_INT_FIBRE_ENERGY BIT(4)
#define MV88E6352_SERDES_INT_STATUS 0x13

#define MV88E6352_SERDES_SPEC_CTRL2 0x1a
#define MV88E6352_SERDES_OUT_AMP_MASK 0x0007

#define MV88E6341_PORT5_LANE 0x15

Expand Down Expand Up @@ -176,6 +178,9 @@ void mv88e6352_serdes_get_regs(struct mv88e6xxx_chip *chip, int port, void *_p);
int mv88e6390_serdes_get_regs_len(struct mv88e6xxx_chip *chip, int port);
void mv88e6390_serdes_get_regs(struct mv88e6xxx_chip *chip, int port, void *_p);

int mv88e6352_serdes_set_tx_amplitude(struct mv88e6xxx_chip *chip, int port,
int val);

/* Return the (first) SERDES lane address a port is using, -errno otherwise. */
static inline int mv88e6xxx_serdes_get_lane(struct mv88e6xxx_chip *chip,
int port)
Expand Down

0 comments on commit 926eae6

Please sign in to comment.