Commit 4cb50d00 authored by David S. Miller's avatar David S. Miller

Merge branch 'mv88e6393x'

Marek Behún says:

====================
Add support for mv88e6393x family of Marvell

after 2 months I finally had time to send v17 of Amethyst patches.

This series is tested on Marvell CN9130-CRB.

Changes since v16:
- dropped patches adding 5gbase-r, since they are already merged
- rebased onto net-next/master
- driver API renamed set_egress_flood() method into 2 methods for
  ucast/mcast floods, so this is fixed

Changes from v15:
- put 10000baseKR_Full back into phylink_validate method for Amethyst,
  it seems I misunderstood the meaning behind things and removed it
  from v15
- removed erratum 3.7, since the procedure is done anyway in
  mv88e6390_serdes_pcs_config
- renumbered errata 3.6 and 3.8 to 4.6 and 4.8, according to newer
  version of the errata document
- refactored errata code a little and removed duplicate macro
  definitions (for example MV88E6390_SGMII_CONTROL is already called
  MV88E6390_SGMII_BMCR)

Changes from v14:
- added my Signed-off-by tags to Pavana's patches, since I am sending
  them (as suggested by Andrew)
- added documentation to second patch adding 5gbase-r mode (as requested
  by Russell)
- added Reviewed-by tags
- applied Vladimir's suggestions:
  - reduced indentation level in mv88e6xxx_set_egress_port and
    mv88e6393x_serdes_port_config
  - removed 10000baseKR_Full from mv88e6393x_phylink_validate
  - removed PHY_INTERFACE_MODE_10GKR from mv88e6xxx_port_set_cmode

Changes from v13:
- added patch that wraps .set_egress_port into mv88e6xxx_set_egress_port,
  so that we do not have to set chip->*gress_dest_port members in every
  implementation of this method
- for the patch that adds Amethyst support:
  - added more information into commit message
  - added these methods for mv88e6393x_ops:
      .port_sync_link
      .port_setup_message_port
      .port_max_speed_mode (new implementation needed)
      .atu_get_hash
      .atu_set_hash
      .serdes_pcs_config
      .serdes_pcs_an_restart
      .serdes_pcs_link_up
  - this device can set upstream port per port, so implement
      .port_set_upstream_port
    instead of
      .set_cpu_port
  - removed USXGMII cmode (not yet supported, working on it)
  - added debug messages into mv88e6393x_port_set_speed_duplex
  - added Amethyst errata 4.5 (EEE should be disabled on SERDES ports)
  - fixed 5gbase-r serdes configuration and interrupt handling
  - refactored mv88e6393x_serdes_setup_errata
  - refactored mv88e6393x_port_policy_write
- added patch implementing .port_set_policy for Amethyst
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents ac1bbf8a 6584b260
...@@ -485,12 +485,12 @@ static int mv88e6xxx_serdes_pcs_get_state(struct dsa_switch *ds, int port, ...@@ -485,12 +485,12 @@ static int mv88e6xxx_serdes_pcs_get_state(struct dsa_switch *ds, int port,
struct phylink_link_state *state) struct phylink_link_state *state)
{ {
struct mv88e6xxx_chip *chip = ds->priv; struct mv88e6xxx_chip *chip = ds->priv;
u8 lane; int lane;
int err; int err;
mv88e6xxx_reg_lock(chip); mv88e6xxx_reg_lock(chip);
lane = mv88e6xxx_serdes_get_lane(chip, port); lane = mv88e6xxx_serdes_get_lane(chip, port);
if (lane && chip->info->ops->serdes_pcs_get_state) if (lane >= 0 && chip->info->ops->serdes_pcs_get_state)
err = chip->info->ops->serdes_pcs_get_state(chip, port, lane, err = chip->info->ops->serdes_pcs_get_state(chip, port, lane,
state); state);
else else
...@@ -506,11 +506,11 @@ static int mv88e6xxx_serdes_pcs_config(struct mv88e6xxx_chip *chip, int port, ...@@ -506,11 +506,11 @@ static int mv88e6xxx_serdes_pcs_config(struct mv88e6xxx_chip *chip, int port,
const unsigned long *advertise) const unsigned long *advertise)
{ {
const struct mv88e6xxx_ops *ops = chip->info->ops; const struct mv88e6xxx_ops *ops = chip->info->ops;
u8 lane; int lane;
if (ops->serdes_pcs_config) { if (ops->serdes_pcs_config) {
lane = mv88e6xxx_serdes_get_lane(chip, port); lane = mv88e6xxx_serdes_get_lane(chip, port);
if (lane) if (lane >= 0)
return ops->serdes_pcs_config(chip, port, lane, mode, return ops->serdes_pcs_config(chip, port, lane, mode,
interface, advertise); interface, advertise);
} }
...@@ -523,14 +523,14 @@ static void mv88e6xxx_serdes_pcs_an_restart(struct dsa_switch *ds, int port) ...@@ -523,14 +523,14 @@ static void mv88e6xxx_serdes_pcs_an_restart(struct dsa_switch *ds, int port)
struct mv88e6xxx_chip *chip = ds->priv; struct mv88e6xxx_chip *chip = ds->priv;
const struct mv88e6xxx_ops *ops; const struct mv88e6xxx_ops *ops;
int err = 0; int err = 0;
u8 lane; int lane;
ops = chip->info->ops; ops = chip->info->ops;
if (ops->serdes_pcs_an_restart) { if (ops->serdes_pcs_an_restart) {
mv88e6xxx_reg_lock(chip); mv88e6xxx_reg_lock(chip);
lane = mv88e6xxx_serdes_get_lane(chip, port); lane = mv88e6xxx_serdes_get_lane(chip, port);
if (lane) if (lane >= 0)
err = ops->serdes_pcs_an_restart(chip, port, lane); err = ops->serdes_pcs_an_restart(chip, port, lane);
mv88e6xxx_reg_unlock(chip); mv88e6xxx_reg_unlock(chip);
...@@ -544,11 +544,11 @@ static int mv88e6xxx_serdes_pcs_link_up(struct mv88e6xxx_chip *chip, int port, ...@@ -544,11 +544,11 @@ static int mv88e6xxx_serdes_pcs_link_up(struct mv88e6xxx_chip *chip, int port,
int speed, int duplex) int speed, int duplex)
{ {
const struct mv88e6xxx_ops *ops = chip->info->ops; const struct mv88e6xxx_ops *ops = chip->info->ops;
u8 lane; int lane;
if (!phylink_autoneg_inband(mode) && ops->serdes_pcs_link_up) { if (!phylink_autoneg_inband(mode) && ops->serdes_pcs_link_up) {
lane = mv88e6xxx_serdes_get_lane(chip, port); lane = mv88e6xxx_serdes_get_lane(chip, port);
if (lane) if (lane >= 0)
return ops->serdes_pcs_link_up(chip, port, lane, return ops->serdes_pcs_link_up(chip, port, lane,
speed, duplex); speed, duplex);
} }
...@@ -635,6 +635,29 @@ static void mv88e6390x_phylink_validate(struct mv88e6xxx_chip *chip, int port, ...@@ -635,6 +635,29 @@ static void mv88e6390x_phylink_validate(struct mv88e6xxx_chip *chip, int port,
mv88e6390_phylink_validate(chip, port, mask, state); mv88e6390_phylink_validate(chip, port, mask, state);
} }
static void mv88e6393x_phylink_validate(struct mv88e6xxx_chip *chip, int port,
unsigned long *mask,
struct phylink_link_state *state)
{
if (port == 0 || port == 9 || port == 10) {
phylink_set(mask, 10000baseT_Full);
phylink_set(mask, 10000baseKR_Full);
phylink_set(mask, 10000baseCR_Full);
phylink_set(mask, 10000baseSR_Full);
phylink_set(mask, 10000baseLR_Full);
phylink_set(mask, 10000baseLRM_Full);
phylink_set(mask, 10000baseER_Full);
phylink_set(mask, 5000baseT_Full);
phylink_set(mask, 2500baseX_Full);
phylink_set(mask, 2500baseT_Full);
}
phylink_set(mask, 1000baseT_Full);
phylink_set(mask, 1000baseX_Full);
mv88e6065_phylink_validate(chip, port, mask, state);
}
static void mv88e6xxx_validate(struct dsa_switch *ds, int port, static void mv88e6xxx_validate(struct dsa_switch *ds, int port,
unsigned long *supported, unsigned long *supported,
struct phylink_link_state *state) struct phylink_link_state *state)
...@@ -2460,11 +2483,11 @@ static irqreturn_t mv88e6xxx_serdes_irq_thread_fn(int irq, void *dev_id) ...@@ -2460,11 +2483,11 @@ static irqreturn_t mv88e6xxx_serdes_irq_thread_fn(int irq, void *dev_id)
struct mv88e6xxx_chip *chip = mvp->chip; struct mv88e6xxx_chip *chip = mvp->chip;
irqreturn_t ret = IRQ_NONE; irqreturn_t ret = IRQ_NONE;
int port = mvp->port; int port = mvp->port;
u8 lane; int lane;
mv88e6xxx_reg_lock(chip); mv88e6xxx_reg_lock(chip);
lane = mv88e6xxx_serdes_get_lane(chip, port); lane = mv88e6xxx_serdes_get_lane(chip, port);
if (lane) if (lane >= 0)
ret = mv88e6xxx_serdes_irq_status(chip, port, lane); ret = mv88e6xxx_serdes_irq_status(chip, port, lane);
mv88e6xxx_reg_unlock(chip); mv88e6xxx_reg_unlock(chip);
...@@ -2472,7 +2495,7 @@ static irqreturn_t mv88e6xxx_serdes_irq_thread_fn(int irq, void *dev_id) ...@@ -2472,7 +2495,7 @@ static irqreturn_t mv88e6xxx_serdes_irq_thread_fn(int irq, void *dev_id)
} }
static int mv88e6xxx_serdes_irq_request(struct mv88e6xxx_chip *chip, int port, static int mv88e6xxx_serdes_irq_request(struct mv88e6xxx_chip *chip, int port,
u8 lane) int lane)
{ {
struct mv88e6xxx_port *dev_id = &chip->ports[port]; struct mv88e6xxx_port *dev_id = &chip->ports[port];
unsigned int irq; unsigned int irq;
...@@ -2501,7 +2524,7 @@ static int mv88e6xxx_serdes_irq_request(struct mv88e6xxx_chip *chip, int port, ...@@ -2501,7 +2524,7 @@ static int mv88e6xxx_serdes_irq_request(struct mv88e6xxx_chip *chip, int port,
} }
static int mv88e6xxx_serdes_irq_free(struct mv88e6xxx_chip *chip, int port, static int mv88e6xxx_serdes_irq_free(struct mv88e6xxx_chip *chip, int port,
u8 lane) int lane)
{ {
struct mv88e6xxx_port *dev_id = &chip->ports[port]; struct mv88e6xxx_port *dev_id = &chip->ports[port];
unsigned int irq = dev_id->serdes_irq; unsigned int irq = dev_id->serdes_irq;
...@@ -2526,11 +2549,11 @@ static int mv88e6xxx_serdes_irq_free(struct mv88e6xxx_chip *chip, int port, ...@@ -2526,11 +2549,11 @@ static int mv88e6xxx_serdes_irq_free(struct mv88e6xxx_chip *chip, int port,
static int mv88e6xxx_serdes_power(struct mv88e6xxx_chip *chip, int port, static int mv88e6xxx_serdes_power(struct mv88e6xxx_chip *chip, int port,
bool on) bool on)
{ {
u8 lane; int lane;
int err; int err;
lane = mv88e6xxx_serdes_get_lane(chip, port); lane = mv88e6xxx_serdes_get_lane(chip, port);
if (!lane) if (lane < 0)
return 0; return 0;
if (on) { if (on) {
...@@ -2550,6 +2573,27 @@ static int mv88e6xxx_serdes_power(struct mv88e6xxx_chip *chip, int port, ...@@ -2550,6 +2573,27 @@ static int mv88e6xxx_serdes_power(struct mv88e6xxx_chip *chip, int port,
return err; return err;
} }
static int mv88e6xxx_set_egress_port(struct mv88e6xxx_chip *chip,
enum mv88e6xxx_egress_direction direction,
int port)
{
int err;
if (!chip->info->ops->set_egress_port)
return -EOPNOTSUPP;
err = chip->info->ops->set_egress_port(chip, direction, port);
if (err)
return err;
if (direction == MV88E6XXX_EGRESS_DIR_INGRESS)
chip->ingress_dest_port = port;
else
chip->egress_dest_port = port;
return 0;
}
static int mv88e6xxx_setup_upstream_port(struct mv88e6xxx_chip *chip, int port) static int mv88e6xxx_setup_upstream_port(struct mv88e6xxx_chip *chip, int port)
{ {
struct dsa_switch *ds = chip->ds; struct dsa_switch *ds = chip->ds;
...@@ -2572,19 +2616,17 @@ static int mv88e6xxx_setup_upstream_port(struct mv88e6xxx_chip *chip, int port) ...@@ -2572,19 +2616,17 @@ static int mv88e6xxx_setup_upstream_port(struct mv88e6xxx_chip *chip, int port)
return err; return err;
} }
if (chip->info->ops->set_egress_port) { err = mv88e6xxx_set_egress_port(chip,
err = chip->info->ops->set_egress_port(chip,
MV88E6XXX_EGRESS_DIR_INGRESS, MV88E6XXX_EGRESS_DIR_INGRESS,
upstream_port); upstream_port);
if (err) if (err && err != -EOPNOTSUPP)
return err; return err;
err = chip->info->ops->set_egress_port(chip, err = mv88e6xxx_set_egress_port(chip,
MV88E6XXX_EGRESS_DIR_EGRESS, MV88E6XXX_EGRESS_DIR_EGRESS,
upstream_port); upstream_port);
if (err) if (err && err != -EOPNOTSUPP)
return err; return err;
}
} }
return 0; return 0;
...@@ -4570,6 +4612,70 @@ static const struct mv88e6xxx_ops mv88e6390x_ops = { ...@@ -4570,6 +4612,70 @@ static const struct mv88e6xxx_ops mv88e6390x_ops = {
.phylink_validate = mv88e6390x_phylink_validate, .phylink_validate = mv88e6390x_phylink_validate,
}; };
static const struct mv88e6xxx_ops mv88e6393x_ops = {
/* MV88E6XXX_FAMILY_6393 */
.setup_errata = mv88e6393x_serdes_setup_errata,
.irl_init_all = mv88e6390_g2_irl_init_all,
.get_eeprom = mv88e6xxx_g2_get_eeprom8,
.set_eeprom = mv88e6xxx_g2_set_eeprom8,
.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
.phy_read = mv88e6xxx_g2_smi_phy_read,
.phy_write = mv88e6xxx_g2_smi_phy_write,
.port_set_link = mv88e6xxx_port_set_link,
.port_sync_link = mv88e6xxx_port_sync_link,
.port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay,
.port_set_speed_duplex = mv88e6393x_port_set_speed_duplex,
.port_max_speed_mode = mv88e6393x_port_max_speed_mode,
.port_tag_remap = mv88e6390_port_tag_remap,
.port_set_policy = mv88e6393x_port_set_policy,
.port_set_frame_mode = mv88e6351_port_set_frame_mode,
.port_set_ucast_flood = mv88e6352_port_set_ucast_flood,
.port_set_mcast_flood = mv88e6352_port_set_mcast_flood,
.port_set_ether_type = mv88e6393x_port_set_ether_type,
.port_set_jumbo_size = mv88e6165_port_set_jumbo_size,
.port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting,
.port_pause_limit = mv88e6390_port_pause_limit,
.port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit,
.port_disable_pri_override = mv88e6xxx_port_disable_pri_override,
.port_get_cmode = mv88e6352_port_get_cmode,
.port_set_cmode = mv88e6393x_port_set_cmode,
.port_setup_message_port = mv88e6xxx_setup_message_port,
.port_set_upstream_port = mv88e6393x_port_set_upstream_port,
.stats_snapshot = mv88e6390_g1_stats_snapshot,
.stats_set_histogram = mv88e6390_g1_stats_set_histogram,
.stats_get_sset_count = mv88e6320_stats_get_sset_count,
.stats_get_strings = mv88e6320_stats_get_strings,
.stats_get_stats = mv88e6390_stats_get_stats,
/* .set_cpu_port is missing because this family does not support a global
* CPU port, only per port CPU port which is set via
* .port_set_upstream_port method.
*/
.set_egress_port = mv88e6393x_set_egress_port,
.watchdog_ops = &mv88e6390_watchdog_ops,
.mgmt_rsvd2cpu = mv88e6393x_port_mgmt_rsvd2cpu,
.pot_clear = mv88e6xxx_g2_pot_clear,
.reset = mv88e6352_g1_reset,
.rmu_disable = mv88e6390_g1_rmu_disable,
.atu_get_hash = mv88e6165_g1_atu_get_hash,
.atu_set_hash = mv88e6165_g1_atu_set_hash,
.vtu_getnext = mv88e6390_g1_vtu_getnext,
.vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,
.serdes_power = mv88e6393x_serdes_power,
.serdes_get_lane = mv88e6393x_serdes_get_lane,
.serdes_pcs_get_state = mv88e6393x_serdes_pcs_get_state,
.serdes_pcs_config = mv88e6390_serdes_pcs_config,
.serdes_pcs_an_restart = mv88e6390_serdes_pcs_an_restart,
.serdes_pcs_link_up = mv88e6390_serdes_pcs_link_up,
.serdes_irq_mapping = mv88e6390_serdes_irq_mapping,
.serdes_irq_enable = mv88e6393x_serdes_irq_enable,
.serdes_irq_status = mv88e6393x_serdes_irq_status,
/* TODO: serdes stats */
.gpio_ops = &mv88e6352_gpio_ops,
.avb_ops = &mv88e6390_avb_ops,
.ptp_ops = &mv88e6352_ptp_ops,
.phylink_validate = mv88e6393x_phylink_validate,
};
static const struct mv88e6xxx_info mv88e6xxx_table[] = { static const struct mv88e6xxx_info mv88e6xxx_table[] = {
[MV88E6085] = { [MV88E6085] = {
.prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6085, .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6085,
...@@ -4941,6 +5047,52 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { ...@@ -4941,6 +5047,52 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.ops = &mv88e6191_ops, .ops = &mv88e6191_ops,
}, },
[MV88E6191X] = {
.prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6191X,
.family = MV88E6XXX_FAMILY_6393,
.name = "Marvell 88E6191X",
.num_databases = 4096,
.num_ports = 11, /* 10 + Z80 */
.num_internal_phys = 9,
.max_vid = 8191,
.port_base_addr = 0x0,
.phy_base_addr = 0x0,
.global1_addr = 0x1b,
.global2_addr = 0x1c,
.age_time_coeff = 3750,
.g1_irqs = 10,
.g2_irqs = 14,
.atu_move_port_mask = 0x1f,
.pvt = true,
.multi_chip = true,
.tag_protocol = DSA_TAG_PROTO_DSA,
.ptp_support = true,
.ops = &mv88e6393x_ops,
},
[MV88E6193X] = {
.prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6193X,
.family = MV88E6XXX_FAMILY_6393,
.name = "Marvell 88E6193X",
.num_databases = 4096,
.num_ports = 11, /* 10 + Z80 */
.num_internal_phys = 9,
.max_vid = 8191,
.port_base_addr = 0x0,
.phy_base_addr = 0x0,
.global1_addr = 0x1b,
.global2_addr = 0x1c,
.age_time_coeff = 3750,
.g1_irqs = 10,
.g2_irqs = 14,
.atu_move_port_mask = 0x1f,
.pvt = true,
.multi_chip = true,
.tag_protocol = DSA_TAG_PROTO_DSA,
.ptp_support = true,
.ops = &mv88e6393x_ops,
},
[MV88E6220] = { [MV88E6220] = {
.prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6220, .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6220,
.family = MV88E6XXX_FAMILY_6250, .family = MV88E6XXX_FAMILY_6250,
...@@ -5231,6 +5383,29 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { ...@@ -5231,6 +5383,29 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.ptp_support = true, .ptp_support = true,
.ops = &mv88e6390x_ops, .ops = &mv88e6390x_ops,
}, },
[MV88E6393X] = {
.prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6393X,
.family = MV88E6XXX_FAMILY_6393,
.name = "Marvell 88E6393X",
.num_databases = 4096,
.num_ports = 11, /* 10 + Z80 */
.num_internal_phys = 9,
.max_vid = 8191,
.port_base_addr = 0x0,
.phy_base_addr = 0x0,
.global1_addr = 0x1b,
.global2_addr = 0x1c,
.age_time_coeff = 3750,
.g1_irqs = 10,
.g2_irqs = 14,
.atu_move_port_mask = 0x1f,
.pvt = true,
.multi_chip = true,
.tag_protocol = DSA_TAG_PROTO_DSA,
.ptp_support = true,
.ops = &mv88e6393x_ops,
},
}; };
static const struct mv88e6xxx_info *mv88e6xxx_lookup_info(unsigned int prod_num) static const struct mv88e6xxx_info *mv88e6xxx_lookup_info(unsigned int prod_num)
...@@ -5338,9 +5513,6 @@ static int mv88e6xxx_port_mirror_add(struct dsa_switch *ds, int port, ...@@ -5338,9 +5513,6 @@ static int mv88e6xxx_port_mirror_add(struct dsa_switch *ds, int port,
int i; int i;
int err; int err;
if (!chip->info->ops->set_egress_port)
return -EOPNOTSUPP;
mutex_lock(&chip->reg_lock); mutex_lock(&chip->reg_lock);
if ((ingress ? chip->ingress_dest_port : chip->egress_dest_port) != if ((ingress ? chip->ingress_dest_port : chip->egress_dest_port) !=
mirror->to_local_port) { mirror->to_local_port) {
...@@ -5355,9 +5527,8 @@ static int mv88e6xxx_port_mirror_add(struct dsa_switch *ds, int port, ...@@ -5355,9 +5527,8 @@ static int mv88e6xxx_port_mirror_add(struct dsa_switch *ds, int port,
goto out; goto out;
} }
err = chip->info->ops->set_egress_port(chip, err = mv88e6xxx_set_egress_port(chip, direction,
direction, mirror->to_local_port);
mirror->to_local_port);
if (err) if (err)
goto out; goto out;
} }
...@@ -5390,10 +5561,8 @@ static void mv88e6xxx_port_mirror_del(struct dsa_switch *ds, int port, ...@@ -5390,10 +5561,8 @@ static void mv88e6xxx_port_mirror_del(struct dsa_switch *ds, int port,
/* Reset egress port when no other mirror is active */ /* Reset egress port when no other mirror is active */
if (!other_mirrors) { if (!other_mirrors) {
if (chip->info->ops->set_egress_port(chip, if (mv88e6xxx_set_egress_port(chip, direction,
direction, dsa_upstream_port(ds, port)))
dsa_upstream_port(ds,
port)))
dev_err(ds->dev, "failed to set egress port\n"); dev_err(ds->dev, "failed to set egress port\n");
} }
......
...@@ -63,6 +63,8 @@ enum mv88e6xxx_model { ...@@ -63,6 +63,8 @@ enum mv88e6xxx_model {
MV88E6190, MV88E6190,
MV88E6190X, MV88E6190X,
MV88E6191, MV88E6191,
MV88E6191X,
MV88E6193X,
MV88E6220, MV88E6220,
MV88E6240, MV88E6240,
MV88E6250, MV88E6250,
...@@ -75,6 +77,7 @@ enum mv88e6xxx_model { ...@@ -75,6 +77,7 @@ enum mv88e6xxx_model {
MV88E6352, MV88E6352,
MV88E6390, MV88E6390,
MV88E6390X, MV88E6390X,
MV88E6393X,
}; };
enum mv88e6xxx_family { enum mv88e6xxx_family {
...@@ -90,6 +93,7 @@ enum mv88e6xxx_family { ...@@ -90,6 +93,7 @@ enum mv88e6xxx_family {
MV88E6XXX_FAMILY_6351, /* 6171 6175 6350 6351 */ MV88E6XXX_FAMILY_6351, /* 6171 6175 6350 6351 */
MV88E6XXX_FAMILY_6352, /* 6172 6176 6240 6352 */ MV88E6XXX_FAMILY_6352, /* 6172 6176 6240 6352 */
MV88E6XXX_FAMILY_6390, /* 6190 6190X 6191 6290 6390 6390X */ MV88E6XXX_FAMILY_6390, /* 6190 6190X 6191 6290 6390 6390X */
MV88E6XXX_FAMILY_6393, /* 6191X 6193X 6393X */
}; };
struct mv88e6xxx_ops; struct mv88e6xxx_ops;
...@@ -513,30 +517,30 @@ struct mv88e6xxx_ops { ...@@ -513,30 +517,30 @@ struct mv88e6xxx_ops {
int (*mgmt_rsvd2cpu)(struct mv88e6xxx_chip *chip); int (*mgmt_rsvd2cpu)(struct mv88e6xxx_chip *chip);
/* Power on/off a SERDES interface */ /* Power on/off a SERDES interface */
int (*serdes_power)(struct mv88e6xxx_chip *chip, int port, u8 lane, int (*serdes_power)(struct mv88e6xxx_chip *chip, int port, int lane,
bool up); bool up);
/* SERDES lane mapping */ /* SERDES lane mapping */
u8 (*serdes_get_lane)(struct mv88e6xxx_chip *chip, int port); int (*serdes_get_lane)(struct mv88e6xxx_chip *chip, int port);
int (*serdes_pcs_get_state)(struct mv88e6xxx_chip *chip, int port, int (*serdes_pcs_get_state)(struct mv88e6xxx_chip *chip, int port,
u8 lane, struct phylink_link_state *state); int lane, struct phylink_link_state *state);
int (*serdes_pcs_config)(struct mv88e6xxx_chip *chip, int port, int (*serdes_pcs_config)(struct mv88e6xxx_chip *chip, int port,
u8 lane, unsigned int mode, int lane, unsigned int mode,
phy_interface_t interface, phy_interface_t interface,
const unsigned long *advertise); const unsigned long *advertise);
int (*serdes_pcs_an_restart)(struct mv88e6xxx_chip *chip, int port, int (*serdes_pcs_an_restart)(struct mv88e6xxx_chip *chip, int port,
u8 lane); int lane);
int (*serdes_pcs_link_up)(struct mv88e6xxx_chip *chip, int port, int (*serdes_pcs_link_up)(struct mv88e6xxx_chip *chip, int port,
u8 lane, int speed, int duplex); int lane, int speed, int duplex);
/* SERDES interrupt handling */ /* SERDES interrupt handling */
unsigned int (*serdes_irq_mapping)(struct mv88e6xxx_chip *chip, unsigned int (*serdes_irq_mapping)(struct mv88e6xxx_chip *chip,
int port); int port);
int (*serdes_irq_enable)(struct mv88e6xxx_chip *chip, int port, u8 lane, int (*serdes_irq_enable)(struct mv88e6xxx_chip *chip, int port, int lane,
bool enable); bool enable);
irqreturn_t (*serdes_irq_status)(struct mv88e6xxx_chip *chip, int port, irqreturn_t (*serdes_irq_status)(struct mv88e6xxx_chip *chip, int port,
u8 lane); int lane);
/* Statistics from the SERDES interface */ /* Statistics from the SERDES interface */
int (*serdes_get_sset_count)(struct mv88e6xxx_chip *chip, int port); int (*serdes_get_sset_count)(struct mv88e6xxx_chip *chip, int port);
......
...@@ -315,7 +315,6 @@ int mv88e6095_g1_set_egress_port(struct mv88e6xxx_chip *chip, ...@@ -315,7 +315,6 @@ int mv88e6095_g1_set_egress_port(struct mv88e6xxx_chip *chip,
enum mv88e6xxx_egress_direction direction, enum mv88e6xxx_egress_direction direction,
int port) int port)
{ {
int *dest_port_chip;
u16 reg; u16 reg;
int err; int err;
...@@ -325,13 +324,11 @@ int mv88e6095_g1_set_egress_port(struct mv88e6xxx_chip *chip, ...@@ -325,13 +324,11 @@ int mv88e6095_g1_set_egress_port(struct mv88e6xxx_chip *chip,
switch (direction) { switch (direction) {
case MV88E6XXX_EGRESS_DIR_INGRESS: case MV88E6XXX_EGRESS_DIR_INGRESS:
dest_port_chip = &chip->ingress_dest_port;
reg &= ~MV88E6185_G1_MONITOR_CTL_INGRESS_DEST_MASK; reg &= ~MV88E6185_G1_MONITOR_CTL_INGRESS_DEST_MASK;
reg |= port << reg |= port <<
__bf_shf(MV88E6185_G1_MONITOR_CTL_INGRESS_DEST_MASK); __bf_shf(MV88E6185_G1_MONITOR_CTL_INGRESS_DEST_MASK);
break; break;
case MV88E6XXX_EGRESS_DIR_EGRESS: case MV88E6XXX_EGRESS_DIR_EGRESS:
dest_port_chip = &chip->egress_dest_port;
reg &= ~MV88E6185_G1_MONITOR_CTL_EGRESS_DEST_MASK; reg &= ~MV88E6185_G1_MONITOR_CTL_EGRESS_DEST_MASK;
reg |= port << reg |= port <<
__bf_shf(MV88E6185_G1_MONITOR_CTL_EGRESS_DEST_MASK); __bf_shf(MV88E6185_G1_MONITOR_CTL_EGRESS_DEST_MASK);
...@@ -340,11 +337,7 @@ int mv88e6095_g1_set_egress_port(struct mv88e6xxx_chip *chip, ...@@ -340,11 +337,7 @@ int mv88e6095_g1_set_egress_port(struct mv88e6xxx_chip *chip,
return -EINVAL; return -EINVAL;
} }
err = mv88e6xxx_g1_write(chip, MV88E6185_G1_MONITOR_CTL, reg); return mv88e6xxx_g1_write(chip, MV88E6185_G1_MONITOR_CTL, reg);
if (!err)
*dest_port_chip = port;
return err;
} }
/* Older generations also call this the ARP destination. It has been /* Older generations also call this the ARP destination. It has been
...@@ -380,28 +373,20 @@ int mv88e6390_g1_set_egress_port(struct mv88e6xxx_chip *chip, ...@@ -380,28 +373,20 @@ int mv88e6390_g1_set_egress_port(struct mv88e6xxx_chip *chip,
enum mv88e6xxx_egress_direction direction, enum mv88e6xxx_egress_direction direction,
int port) int port)
{ {
int *dest_port_chip;
u16 ptr; u16 ptr;
int err;
switch (direction) { switch (direction) {
case MV88E6XXX_EGRESS_DIR_INGRESS: case MV88E6XXX_EGRESS_DIR_INGRESS:
dest_port_chip = &chip->ingress_dest_port;
ptr = MV88E6390_G1_MONITOR_MGMT_CTL_PTR_INGRESS_DEST; ptr = MV88E6390_G1_MONITOR_MGMT_CTL_PTR_INGRESS_DEST;
break; break;
case MV88E6XXX_EGRESS_DIR_EGRESS: case MV88E6XXX_EGRESS_DIR_EGRESS:
dest_port_chip = &chip->egress_dest_port;
ptr = MV88E6390_G1_MONITOR_MGMT_CTL_PTR_EGRESS_DEST; ptr = MV88E6390_G1_MONITOR_MGMT_CTL_PTR_EGRESS_DEST;
break; break;
default: default:
return -EINVAL; return -EINVAL;
} }
err = mv88e6390_g1_monitor_write(chip, ptr, port); return mv88e6390_g1_monitor_write(chip, ptr, port);
if (!err)
*dest_port_chip = port;
return err;
} }
int mv88e6390_g1_set_cpu_port(struct mv88e6xxx_chip *chip, int port) int mv88e6390_g1_set_cpu_port(struct mv88e6xxx_chip *chip, int port)
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#define MV88E6185_G1_STS_PPU_STATE_DISABLED 0x8000 #define MV88E6185_G1_STS_PPU_STATE_DISABLED 0x8000
#define MV88E6185_G1_STS_PPU_STATE_POLLING 0xc000 #define MV88E6185_G1_STS_PPU_STATE_POLLING 0xc000
#define MV88E6XXX_G1_STS_INIT_READY 0x0800 #define MV88E6XXX_G1_STS_INIT_READY 0x0800
#define MV88E6393X_G1_STS_IRQ_DEVICE_2 9
#define MV88E6XXX_G1_STS_IRQ_AVB 8 #define MV88E6XXX_G1_STS_IRQ_AVB 8
#define MV88E6XXX_G1_STS_IRQ_DEVICE 7 #define MV88E6XXX_G1_STS_IRQ_DEVICE 7
#define MV88E6XXX_G1_STS_IRQ_STATS 6 #define MV88E6XXX_G1_STS_IRQ_STATS 6
...@@ -59,6 +60,7 @@ ...@@ -59,6 +60,7 @@
#define MV88E6185_G1_CTL1_SCHED_PRIO 0x0800 #define MV88E6185_G1_CTL1_SCHED_PRIO 0x0800
#define MV88E6185_G1_CTL1_MAX_FRAME_1632 0x0400 #define MV88E6185_G1_CTL1_MAX_FRAME_1632 0x0400
#define MV88E6185_G1_CTL1_RELOAD_EEPROM 0x0200 #define MV88E6185_G1_CTL1_RELOAD_EEPROM 0x0200
#define MV88E6393X_G1_CTL1_DEVICE2_EN 0x0200
#define MV88E6XXX_G1_CTL1_DEVICE_EN 0x0080 #define MV88E6XXX_G1_CTL1_DEVICE_EN 0x0080
#define MV88E6XXX_G1_CTL1_STATS_DONE_EN 0x0040 #define MV88E6XXX_G1_CTL1_STATS_DONE_EN 0x0040
#define MV88E6XXX_G1_CTL1_VTU_PROBLEM_EN 0x0020 #define MV88E6XXX_G1_CTL1_VTU_PROBLEM_EN 0x0020
......
...@@ -38,9 +38,15 @@ ...@@ -38,9 +38,15 @@
/* Offset 0x02: MGMT Enable Register 2x */ /* Offset 0x02: MGMT Enable Register 2x */
#define MV88E6XXX_G2_MGMT_EN_2X 0x02 #define MV88E6XXX_G2_MGMT_EN_2X 0x02
/* Offset 0x02: MAC LINK change IRQ Register for MV88E6393X */
#define MV88E6393X_G2_MACLINK_INT_SRC 0x02
/* Offset 0x03: MGMT Enable Register 0x */ /* Offset 0x03: MGMT Enable Register 0x */
#define MV88E6XXX_G2_MGMT_EN_0X 0x03 #define MV88E6XXX_G2_MGMT_EN_0X 0x03
/* Offset 0x03: MAC LINK change IRQ Mask Register for MV88E6393X */
#define MV88E6393X_G2_MACLINK_INT_MASK 0x03
/* Offset 0x04: Flow Control Delay Register */ /* Offset 0x04: Flow Control Delay Register */
#define MV88E6XXX_G2_FLOW_CTL 0x04 #define MV88E6XXX_G2_FLOW_CTL 0x04
...@@ -52,6 +58,8 @@ ...@@ -52,6 +58,8 @@
#define MV88E6XXX_G2_SWITCH_MGMT_FORCE_FLOW_CTL_PRI 0x0080 #define MV88E6XXX_G2_SWITCH_MGMT_FORCE_FLOW_CTL_PRI 0x0080
#define MV88E6XXX_G2_SWITCH_MGMT_RSVD2CPU 0x0008 #define MV88E6XXX_G2_SWITCH_MGMT_RSVD2CPU 0x0008
#define MV88E6393X_G2_EGRESS_MONITOR_DEST 0x05
/* Offset 0x06: Device Mapping Table Register */ /* Offset 0x06: Device Mapping Table Register */
#define MV88E6XXX_G2_DEVICE_MAPPING 0x06 #define MV88E6XXX_G2_DEVICE_MAPPING 0x06
#define MV88E6XXX_G2_DEVICE_MAPPING_UPDATE 0x8000 #define MV88E6XXX_G2_DEVICE_MAPPING_UPDATE 0x8000
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include <linux/phylink.h> #include <linux/phylink.h>
#include "chip.h" #include "chip.h"
#include "global2.h"
#include "port.h" #include "port.h"
#include "serdes.h" #include "serdes.h"
...@@ -25,6 +26,14 @@ int mv88e6xxx_port_read(struct mv88e6xxx_chip *chip, int port, int reg, ...@@ -25,6 +26,14 @@ int mv88e6xxx_port_read(struct mv88e6xxx_chip *chip, int port, int reg,
return mv88e6xxx_read(chip, addr, reg, val); return mv88e6xxx_read(chip, addr, reg, val);
} }
int mv88e6xxx_port_wait_bit(struct mv88e6xxx_chip *chip, int port, int reg,
int bit, int val)
{
int addr = chip->info->port_base_addr + port;
return mv88e6xxx_wait_bit(chip, addr, reg, bit, val);
}
int mv88e6xxx_port_write(struct mv88e6xxx_chip *chip, int port, int reg, int mv88e6xxx_port_write(struct mv88e6xxx_chip *chip, int port, int reg,
u16 val) u16 val)
{ {
...@@ -426,11 +435,111 @@ phy_interface_t mv88e6390x_port_max_speed_mode(int port) ...@@ -426,11 +435,111 @@ phy_interface_t mv88e6390x_port_max_speed_mode(int port)
return PHY_INTERFACE_MODE_NA; return PHY_INTERFACE_MODE_NA;
} }
/* Support 10, 100, 200, 1000, 2500, 5000, 10000 Mbps (e.g. 88E6393X)
* Function mv88e6xxx_port_set_speed_duplex() can't be used as the register
* values for speeds 2500 & 5000 conflict.
*/
int mv88e6393x_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port,
int speed, int duplex)
{
u16 reg, ctrl;
int err;
if (speed == SPEED_MAX)
speed = (port > 0 && port < 9) ? 1000 : 10000;
if (speed == 200 && port != 0)
return -EOPNOTSUPP;
if (speed >= 2500 && port > 0 && port < 9)
return -EOPNOTSUPP;
switch (speed) {
case 10:
ctrl = MV88E6XXX_PORT_MAC_CTL_SPEED_10;
break;
case 100:
ctrl = MV88E6XXX_PORT_MAC_CTL_SPEED_100;
break;
case 200:
ctrl = MV88E6XXX_PORT_MAC_CTL_SPEED_100 |
MV88E6390_PORT_MAC_CTL_ALTSPEED;
break;
case 1000:
ctrl = MV88E6XXX_PORT_MAC_CTL_SPEED_1000;
break;
case 2500:
ctrl = MV88E6XXX_PORT_MAC_CTL_SPEED_1000 |
MV88E6390_PORT_MAC_CTL_ALTSPEED;
break;
case 5000:
ctrl = MV88E6390_PORT_MAC_CTL_SPEED_10000 |
MV88E6390_PORT_MAC_CTL_ALTSPEED;
break;
case 10000:
case SPEED_UNFORCED:
ctrl = MV88E6XXX_PORT_MAC_CTL_SPEED_UNFORCED;
break;
default:
return -EOPNOTSUPP;
}
switch (duplex) {
case DUPLEX_HALF:
ctrl |= MV88E6XXX_PORT_MAC_CTL_FORCE_DUPLEX;
break;
case DUPLEX_FULL:
ctrl |= MV88E6XXX_PORT_MAC_CTL_FORCE_DUPLEX |
MV88E6XXX_PORT_MAC_CTL_DUPLEX_FULL;
break;
case DUPLEX_UNFORCED:
/* normal duplex detection */
break;
default:
return -EOPNOTSUPP;
}
err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_MAC_CTL, &reg);
if (err)
return err;
reg &= ~(MV88E6XXX_PORT_MAC_CTL_SPEED_MASK |
MV88E6390_PORT_MAC_CTL_ALTSPEED |
MV88E6390_PORT_MAC_CTL_FORCE_SPEED);
if (speed != SPEED_UNFORCED)
reg |= MV88E6390_PORT_MAC_CTL_FORCE_SPEED;
reg |= ctrl;
err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_MAC_CTL, reg);
if (err)
return err;
if (speed)
dev_dbg(chip->dev, "p%d: Speed set to %d Mbps\n", port, speed);
else
dev_dbg(chip->dev, "p%d: Speed unforced\n", port);
dev_dbg(chip->dev, "p%d: %s %s duplex\n", port,
reg & MV88E6XXX_PORT_MAC_CTL_FORCE_DUPLEX ? "Force" : "Unforce",
reg & MV88E6XXX_PORT_MAC_CTL_DUPLEX_FULL ? "full" : "half");
return 0;
}
phy_interface_t mv88e6393x_port_max_speed_mode(int port)
{
if (port == 0 || port == 9 || port == 10)
return PHY_INTERFACE_MODE_10GBASER;
return PHY_INTERFACE_MODE_NA;
}
static int mv88e6xxx_port_set_cmode(struct mv88e6xxx_chip *chip, int port, static int mv88e6xxx_port_set_cmode(struct mv88e6xxx_chip *chip, int port,
phy_interface_t mode, bool force) phy_interface_t mode, bool force)
{ {
u8 lane;
u16 cmode; u16 cmode;
int lane;
u16 reg; u16 reg;
int err; int err;
...@@ -450,6 +559,9 @@ static int mv88e6xxx_port_set_cmode(struct mv88e6xxx_chip *chip, int port, ...@@ -450,6 +559,9 @@ static int mv88e6xxx_port_set_cmode(struct mv88e6xxx_chip *chip, int port,
case PHY_INTERFACE_MODE_2500BASEX: case PHY_INTERFACE_MODE_2500BASEX:
cmode = MV88E6XXX_PORT_STS_CMODE_2500BASEX; cmode = MV88E6XXX_PORT_STS_CMODE_2500BASEX;
break; break;
case PHY_INTERFACE_MODE_5GBASER:
cmode = MV88E6393X_PORT_STS_CMODE_5GBASER;
break;
case PHY_INTERFACE_MODE_XGMII: case PHY_INTERFACE_MODE_XGMII:
case PHY_INTERFACE_MODE_XAUI: case PHY_INTERFACE_MODE_XAUI:
cmode = MV88E6XXX_PORT_STS_CMODE_XAUI; cmode = MV88E6XXX_PORT_STS_CMODE_XAUI;
...@@ -457,6 +569,9 @@ static int mv88e6xxx_port_set_cmode(struct mv88e6xxx_chip *chip, int port, ...@@ -457,6 +569,9 @@ static int mv88e6xxx_port_set_cmode(struct mv88e6xxx_chip *chip, int port,
case PHY_INTERFACE_MODE_RXAUI: case PHY_INTERFACE_MODE_RXAUI:
cmode = MV88E6XXX_PORT_STS_CMODE_RXAUI; cmode = MV88E6XXX_PORT_STS_CMODE_RXAUI;
break; break;
case PHY_INTERFACE_MODE_10GBASER:
cmode = MV88E6393X_PORT_STS_CMODE_10GBASER;
break;
default: default:
cmode = 0; cmode = 0;
} }
...@@ -466,7 +581,7 @@ static int mv88e6xxx_port_set_cmode(struct mv88e6xxx_chip *chip, int port, ...@@ -466,7 +581,7 @@ static int mv88e6xxx_port_set_cmode(struct mv88e6xxx_chip *chip, int port,
return 0; return 0;
lane = mv88e6xxx_serdes_get_lane(chip, port); lane = mv88e6xxx_serdes_get_lane(chip, port);
if (lane) { if (lane >= 0) {
if (chip->ports[port].serdes_irq) { if (chip->ports[port].serdes_irq) {
err = mv88e6xxx_serdes_irq_disable(chip, port, lane); err = mv88e6xxx_serdes_irq_disable(chip, port, lane);
if (err) if (err)
...@@ -495,8 +610,8 @@ static int mv88e6xxx_port_set_cmode(struct mv88e6xxx_chip *chip, int port, ...@@ -495,8 +610,8 @@ static int mv88e6xxx_port_set_cmode(struct mv88e6xxx_chip *chip, int port,
chip->ports[port].cmode = cmode; chip->ports[port].cmode = cmode;
lane = mv88e6xxx_serdes_get_lane(chip, port); lane = mv88e6xxx_serdes_get_lane(chip, port);
if (!lane) if (lane < 0)
return -ENODEV; return lane;
err = mv88e6xxx_serdes_power_up(chip, port, lane); err = mv88e6xxx_serdes_power_up(chip, port, lane);
if (err) if (err)
...@@ -541,6 +656,29 @@ int mv88e6390_port_set_cmode(struct mv88e6xxx_chip *chip, int port, ...@@ -541,6 +656,29 @@ int mv88e6390_port_set_cmode(struct mv88e6xxx_chip *chip, int port,
return mv88e6xxx_port_set_cmode(chip, port, mode, false); return mv88e6xxx_port_set_cmode(chip, port, mode, false);
} }
int mv88e6393x_port_set_cmode(struct mv88e6xxx_chip *chip, int port,
phy_interface_t mode)
{
int err;
u16 reg;
if (port != 0 && port != 9 && port != 10)
return -EOPNOTSUPP;
/* mv88e6393x errata 4.5: EEE should be disabled on SERDES ports */
err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_MAC_CTL, &reg);
if (err)
return err;
reg &= ~MV88E6XXX_PORT_MAC_CTL_EEE;
reg |= MV88E6XXX_PORT_MAC_CTL_FORCE_EEE;
err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_MAC_CTL, reg);
if (err)
return err;
return mv88e6xxx_port_set_cmode(chip, port, mode, false);
}
static int mv88e6341_port_set_cmode_writable(struct mv88e6xxx_chip *chip, static int mv88e6341_port_set_cmode_writable(struct mv88e6xxx_chip *chip,
int port) int port)
{ {
...@@ -1185,6 +1323,156 @@ int mv88e6xxx_port_disable_pri_override(struct mv88e6xxx_chip *chip, int port) ...@@ -1185,6 +1323,156 @@ int mv88e6xxx_port_disable_pri_override(struct mv88e6xxx_chip *chip, int port)
return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_PRI_OVERRIDE, 0); return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_PRI_OVERRIDE, 0);
} }
/* Offset 0x0E: Policy & MGMT Control Register for FAMILY 6191X 6193X 6393X */
static int mv88e6393x_port_policy_read(struct mv88e6xxx_chip *chip, int port,
u16 pointer, u8 *data)
{
u16 reg;
int err;
err = mv88e6xxx_port_write(chip, port, MV88E6393X_PORT_POLICY_MGMT_CTL,
pointer);
if (err)
return err;
err = mv88e6xxx_port_read(chip, port, MV88E6393X_PORT_POLICY_MGMT_CTL,
&reg);
if (err)
return err;
*data = reg;
return 0;
}
static int mv88e6393x_port_policy_write(struct mv88e6xxx_chip *chip, int port,
u16 pointer, u8 data)
{
u16 reg;
reg = MV88E6393X_PORT_POLICY_MGMT_CTL_UPDATE | pointer | data;
return mv88e6xxx_port_write(chip, port, MV88E6393X_PORT_POLICY_MGMT_CTL,
reg);
}
static int mv88e6393x_port_policy_write_all(struct mv88e6xxx_chip *chip,
u16 pointer, u8 data)
{
int err, port;
for (port = 0; port < mv88e6xxx_num_ports(chip); port++) {
if (dsa_is_unused_port(chip->ds, port))
continue;
err = mv88e6393x_port_policy_write(chip, port, pointer, data);
if (err)
return err;
}
return 0;
}
int mv88e6393x_set_egress_port(struct mv88e6xxx_chip *chip,
enum mv88e6xxx_egress_direction direction,
int port)
{
u16 ptr;
int err;
switch (direction) {
case MV88E6XXX_EGRESS_DIR_INGRESS:
ptr = MV88E6393X_PORT_POLICY_MGMT_CTL_PTR_INGRESS_DEST;
err = mv88e6393x_port_policy_write_all(chip, ptr, port);
if (err)
return err;
break;
case MV88E6XXX_EGRESS_DIR_EGRESS:
ptr = MV88E6393X_G2_EGRESS_MONITOR_DEST;
err = mv88e6xxx_g2_write(chip, ptr, port);
if (err)
return err;
break;
}
return 0;
}
int mv88e6393x_port_set_upstream_port(struct mv88e6xxx_chip *chip, int port,
int upstream_port)
{
u16 ptr = MV88E6393X_PORT_POLICY_MGMT_CTL_PTR_CPU_DEST;
u8 data = MV88E6393X_PORT_POLICY_MGMT_CTL_CPU_DEST_MGMTPRI |
upstream_port;
return mv88e6393x_port_policy_write(chip, port, ptr, data);
}
int mv88e6393x_port_mgmt_rsvd2cpu(struct mv88e6xxx_chip *chip)
{
u16 ptr;
int err;
/* Consider the frames with reserved multicast destination
* addresses matching 01:80:c2:00:00:00 and
* 01:80:c2:00:00:02 as MGMT.
*/
ptr = MV88E6393X_PORT_POLICY_MGMT_CTL_PTR_01C280000000XLO;
err = mv88e6393x_port_policy_write_all(chip, ptr, 0xff);
if (err)
return err;
ptr = MV88E6393X_PORT_POLICY_MGMT_CTL_PTR_01C280000000XHI;
err = mv88e6393x_port_policy_write_all(chip, ptr, 0xff);
if (err)
return err;
ptr = MV88E6393X_PORT_POLICY_MGMT_CTL_PTR_01C280000002XLO;
err = mv88e6393x_port_policy_write_all(chip, ptr, 0xff);
if (err)
return err;
ptr = MV88E6393X_PORT_POLICY_MGMT_CTL_PTR_01C280000002XHI;
err = mv88e6393x_port_policy_write_all(chip, ptr, 0xff);
if (err)
return err;
return 0;
}
/* Offset 0x10 & 0x11: EPC */
static int mv88e6393x_port_epc_wait_ready(struct mv88e6xxx_chip *chip, int port)
{
int bit = __bf_shf(MV88E6393X_PORT_EPC_CMD_BUSY);
return mv88e6xxx_port_wait_bit(chip, port, MV88E6393X_PORT_EPC_CMD, bit, 0);
}
/* Port Ether type for 6393X family */
int mv88e6393x_port_set_ether_type(struct mv88e6xxx_chip *chip, int port,
u16 etype)
{
u16 val;
int err;
err = mv88e6393x_port_epc_wait_ready(chip, port);
if (err)
return err;
err = mv88e6xxx_port_write(chip, port, MV88E6393X_PORT_EPC_DATA, etype);
if (err)
return err;
val = MV88E6393X_PORT_EPC_CMD_BUSY |
MV88E6393X_PORT_EPC_CMD_WRITE |
MV88E6393X_PORT_EPC_INDEX_PORT_ETYPE;
return mv88e6xxx_port_write(chip, port, MV88E6393X_PORT_EPC_CMD, val);
}
/* Offset 0x0f: Port Ether type */ /* Offset 0x0f: Port Ether type */
int mv88e6351_port_set_ether_type(struct mv88e6xxx_chip *chip, int port, int mv88e6351_port_set_ether_type(struct mv88e6xxx_chip *chip, int port,
...@@ -1259,46 +1547,43 @@ int mv88e6390_port_tag_remap(struct mv88e6xxx_chip *chip, int port) ...@@ -1259,46 +1547,43 @@ int mv88e6390_port_tag_remap(struct mv88e6xxx_chip *chip, int port)
/* Offset 0x0E: Policy Control Register */ /* Offset 0x0E: Policy Control Register */
int mv88e6352_port_set_policy(struct mv88e6xxx_chip *chip, int port, static int
enum mv88e6xxx_policy_mapping mapping, mv88e6xxx_port_policy_mapping_get_pos(enum mv88e6xxx_policy_mapping mapping,
enum mv88e6xxx_policy_action action) enum mv88e6xxx_policy_action action,
u16 *mask, u16 *val, int *shift)
{ {
u16 reg, mask, val;
int shift;
int err;
switch (mapping) { switch (mapping) {
case MV88E6XXX_POLICY_MAPPING_DA: case MV88E6XXX_POLICY_MAPPING_DA:
shift = __bf_shf(MV88E6XXX_PORT_POLICY_CTL_DA_MASK); *shift = __bf_shf(MV88E6XXX_PORT_POLICY_CTL_DA_MASK);
mask = MV88E6XXX_PORT_POLICY_CTL_DA_MASK; *mask = MV88E6XXX_PORT_POLICY_CTL_DA_MASK;
break; break;
case MV88E6XXX_POLICY_MAPPING_SA: case MV88E6XXX_POLICY_MAPPING_SA:
shift = __bf_shf(MV88E6XXX_PORT_POLICY_CTL_SA_MASK); *shift = __bf_shf(MV88E6XXX_PORT_POLICY_CTL_SA_MASK);
mask = MV88E6XXX_PORT_POLICY_CTL_SA_MASK; *mask = MV88E6XXX_PORT_POLICY_CTL_SA_MASK;
break; break;
case MV88E6XXX_POLICY_MAPPING_VTU: case MV88E6XXX_POLICY_MAPPING_VTU:
shift = __bf_shf(MV88E6XXX_PORT_POLICY_CTL_VTU_MASK); *shift = __bf_shf(MV88E6XXX_PORT_POLICY_CTL_VTU_MASK);
mask = MV88E6XXX_PORT_POLICY_CTL_VTU_MASK; *mask = MV88E6XXX_PORT_POLICY_CTL_VTU_MASK;
break; break;
case MV88E6XXX_POLICY_MAPPING_ETYPE: case MV88E6XXX_POLICY_MAPPING_ETYPE:
shift = __bf_shf(MV88E6XXX_PORT_POLICY_CTL_ETYPE_MASK); *shift = __bf_shf(MV88E6XXX_PORT_POLICY_CTL_ETYPE_MASK);
mask = MV88E6XXX_PORT_POLICY_CTL_ETYPE_MASK; *mask = MV88E6XXX_PORT_POLICY_CTL_ETYPE_MASK;
break; break;
case MV88E6XXX_POLICY_MAPPING_PPPOE: case MV88E6XXX_POLICY_MAPPING_PPPOE:
shift = __bf_shf(MV88E6XXX_PORT_POLICY_CTL_PPPOE_MASK); *shift = __bf_shf(MV88E6XXX_PORT_POLICY_CTL_PPPOE_MASK);
mask = MV88E6XXX_PORT_POLICY_CTL_PPPOE_MASK; *mask = MV88E6XXX_PORT_POLICY_CTL_PPPOE_MASK;
break; break;
case MV88E6XXX_POLICY_MAPPING_VBAS: case MV88E6XXX_POLICY_MAPPING_VBAS:
shift = __bf_shf(MV88E6XXX_PORT_POLICY_CTL_VBAS_MASK); *shift = __bf_shf(MV88E6XXX_PORT_POLICY_CTL_VBAS_MASK);
mask = MV88E6XXX_PORT_POLICY_CTL_VBAS_MASK; *mask = MV88E6XXX_PORT_POLICY_CTL_VBAS_MASK;
break; break;
case MV88E6XXX_POLICY_MAPPING_OPT82: case MV88E6XXX_POLICY_MAPPING_OPT82:
shift = __bf_shf(MV88E6XXX_PORT_POLICY_CTL_OPT82_MASK); *shift = __bf_shf(MV88E6XXX_PORT_POLICY_CTL_OPT82_MASK);
mask = MV88E6XXX_PORT_POLICY_CTL_OPT82_MASK; *mask = MV88E6XXX_PORT_POLICY_CTL_OPT82_MASK;
break; break;
case MV88E6XXX_POLICY_MAPPING_UDP: case MV88E6XXX_POLICY_MAPPING_UDP:
shift = __bf_shf(MV88E6XXX_PORT_POLICY_CTL_UDP_MASK); *shift = __bf_shf(MV88E6XXX_PORT_POLICY_CTL_UDP_MASK);
mask = MV88E6XXX_PORT_POLICY_CTL_UDP_MASK; *mask = MV88E6XXX_PORT_POLICY_CTL_UDP_MASK;
break; break;
default: default:
return -EOPNOTSUPP; return -EOPNOTSUPP;
...@@ -1306,21 +1591,37 @@ int mv88e6352_port_set_policy(struct mv88e6xxx_chip *chip, int port, ...@@ -1306,21 +1591,37 @@ int mv88e6352_port_set_policy(struct mv88e6xxx_chip *chip, int port,
switch (action) { switch (action) {
case MV88E6XXX_POLICY_ACTION_NORMAL: case MV88E6XXX_POLICY_ACTION_NORMAL:
val = MV88E6XXX_PORT_POLICY_CTL_NORMAL; *val = MV88E6XXX_PORT_POLICY_CTL_NORMAL;
break; break;
case MV88E6XXX_POLICY_ACTION_MIRROR: case MV88E6XXX_POLICY_ACTION_MIRROR:
val = MV88E6XXX_PORT_POLICY_CTL_MIRROR; *val = MV88E6XXX_PORT_POLICY_CTL_MIRROR;
break; break;
case MV88E6XXX_POLICY_ACTION_TRAP: case MV88E6XXX_POLICY_ACTION_TRAP:
val = MV88E6XXX_PORT_POLICY_CTL_TRAP; *val = MV88E6XXX_PORT_POLICY_CTL_TRAP;
break; break;
case MV88E6XXX_POLICY_ACTION_DISCARD: case MV88E6XXX_POLICY_ACTION_DISCARD:
val = MV88E6XXX_PORT_POLICY_CTL_DISCARD; *val = MV88E6XXX_PORT_POLICY_CTL_DISCARD;
break; break;
default: default:
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
return 0;
}
int mv88e6352_port_set_policy(struct mv88e6xxx_chip *chip, int port,
enum mv88e6xxx_policy_mapping mapping,
enum mv88e6xxx_policy_action action)
{
u16 reg, mask, val;
int shift;
int err;
err = mv88e6xxx_port_policy_mapping_get_pos(mapping, action, &mask,
&val, &shift);
if (err)
return err;
err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_POLICY_CTL, &reg); err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_POLICY_CTL, &reg);
if (err) if (err)
return err; return err;
...@@ -1330,3 +1631,37 @@ int mv88e6352_port_set_policy(struct mv88e6xxx_chip *chip, int port, ...@@ -1330,3 +1631,37 @@ int mv88e6352_port_set_policy(struct mv88e6xxx_chip *chip, int port,
return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_POLICY_CTL, reg); return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_POLICY_CTL, reg);
} }
int mv88e6393x_port_set_policy(struct mv88e6xxx_chip *chip, int port,
enum mv88e6xxx_policy_mapping mapping,
enum mv88e6xxx_policy_action action)
{
u16 mask, val;
int shift;
int err;
u16 ptr;
u8 reg;
err = mv88e6xxx_port_policy_mapping_get_pos(mapping, action, &mask,
&val, &shift);
if (err)
return err;
/* The 16-bit Port Policy CTL register from older chips is on 6393x
* changed to Port Policy MGMT CTL, which can access more data, but
* indirectly. The original 16-bit value is divided into two 8-bit
* registers.
*/
ptr = shift / 8;
shift %= 8;
mask >>= ptr * 8;
err = mv88e6393x_port_policy_read(chip, port, ptr, &reg);
if (err)
return err;
reg &= ~mask;
reg |= (val << shift) & mask;
return mv88e6393x_port_policy_write(chip, port, ptr, reg);
}
...@@ -49,6 +49,9 @@ ...@@ -49,6 +49,9 @@
#define MV88E6XXX_PORT_STS_CMODE_2500BASEX 0x000b #define MV88E6XXX_PORT_STS_CMODE_2500BASEX 0x000b
#define MV88E6XXX_PORT_STS_CMODE_XAUI 0x000c #define MV88E6XXX_PORT_STS_CMODE_XAUI 0x000c
#define MV88E6XXX_PORT_STS_CMODE_RXAUI 0x000d #define MV88E6XXX_PORT_STS_CMODE_RXAUI 0x000d
#define MV88E6393X_PORT_STS_CMODE_5GBASER 0x000c
#define MV88E6393X_PORT_STS_CMODE_10GBASER 0x000d
#define MV88E6393X_PORT_STS_CMODE_USXGMII 0x000e
#define MV88E6185_PORT_STS_CDUPLEX 0x0008 #define MV88E6185_PORT_STS_CDUPLEX 0x0008
#define MV88E6185_PORT_STS_CMODE_MASK 0x0007 #define MV88E6185_PORT_STS_CMODE_MASK 0x0007
#define MV88E6185_PORT_STS_CMODE_GMII_FD 0x0000 #define MV88E6185_PORT_STS_CMODE_GMII_FD 0x0000
...@@ -68,6 +71,8 @@ ...@@ -68,6 +71,8 @@
#define MV88E6390_PORT_MAC_CTL_FORCE_SPEED 0x2000 #define MV88E6390_PORT_MAC_CTL_FORCE_SPEED 0x2000
#define MV88E6390_PORT_MAC_CTL_ALTSPEED 0x1000 #define MV88E6390_PORT_MAC_CTL_ALTSPEED 0x1000
#define MV88E6352_PORT_MAC_CTL_200BASE 0x1000 #define MV88E6352_PORT_MAC_CTL_200BASE 0x1000
#define MV88E6XXX_PORT_MAC_CTL_EEE 0x0200
#define MV88E6XXX_PORT_MAC_CTL_FORCE_EEE 0x0100
#define MV88E6185_PORT_MAC_CTL_AN_EN 0x0400 #define MV88E6185_PORT_MAC_CTL_AN_EN 0x0400
#define MV88E6185_PORT_MAC_CTL_AN_RESTART 0x0200 #define MV88E6185_PORT_MAC_CTL_AN_RESTART 0x0200
#define MV88E6185_PORT_MAC_CTL_AN_DONE 0x0100 #define MV88E6185_PORT_MAC_CTL_AN_DONE 0x0100
...@@ -117,6 +122,8 @@ ...@@ -117,6 +122,8 @@
#define MV88E6XXX_PORT_SWITCH_ID_PROD_6176 0x1760 #define MV88E6XXX_PORT_SWITCH_ID_PROD_6176 0x1760
#define MV88E6XXX_PORT_SWITCH_ID_PROD_6190 0x1900 #define MV88E6XXX_PORT_SWITCH_ID_PROD_6190 0x1900
#define MV88E6XXX_PORT_SWITCH_ID_PROD_6191 0x1910 #define MV88E6XXX_PORT_SWITCH_ID_PROD_6191 0x1910
#define MV88E6XXX_PORT_SWITCH_ID_PROD_6191X 0x1920
#define MV88E6XXX_PORT_SWITCH_ID_PROD_6193X 0x1930
#define MV88E6XXX_PORT_SWITCH_ID_PROD_6185 0x1a70 #define MV88E6XXX_PORT_SWITCH_ID_PROD_6185 0x1a70
#define MV88E6XXX_PORT_SWITCH_ID_PROD_6220 0x2200 #define MV88E6XXX_PORT_SWITCH_ID_PROD_6220 0x2200
#define MV88E6XXX_PORT_SWITCH_ID_PROD_6240 0x2400 #define MV88E6XXX_PORT_SWITCH_ID_PROD_6240 0x2400
...@@ -129,6 +136,7 @@ ...@@ -129,6 +136,7 @@
#define MV88E6XXX_PORT_SWITCH_ID_PROD_6350 0x3710 #define MV88E6XXX_PORT_SWITCH_ID_PROD_6350 0x3710
#define MV88E6XXX_PORT_SWITCH_ID_PROD_6351 0x3750 #define MV88E6XXX_PORT_SWITCH_ID_PROD_6351 0x3750
#define MV88E6XXX_PORT_SWITCH_ID_PROD_6390 0x3900 #define MV88E6XXX_PORT_SWITCH_ID_PROD_6390 0x3900
#define MV88E6XXX_PORT_SWITCH_ID_PROD_6393X 0x3930
#define MV88E6XXX_PORT_SWITCH_ID_REV_MASK 0x000f #define MV88E6XXX_PORT_SWITCH_ID_REV_MASK 0x000f
/* Offset 0x04: Port Control Register */ /* Offset 0x04: Port Control Register */
...@@ -236,6 +244,19 @@ ...@@ -236,6 +244,19 @@
#define MV88E6XXX_PORT_POLICY_CTL_TRAP 0x0002 #define MV88E6XXX_PORT_POLICY_CTL_TRAP 0x0002
#define MV88E6XXX_PORT_POLICY_CTL_DISCARD 0x0003 #define MV88E6XXX_PORT_POLICY_CTL_DISCARD 0x0003
/* Offset 0x0E: Policy & MGMT Control Register (FAMILY_6393X) */
#define MV88E6393X_PORT_POLICY_MGMT_CTL 0x0e
#define MV88E6393X_PORT_POLICY_MGMT_CTL_UPDATE 0x8000
#define MV88E6393X_PORT_POLICY_MGMT_CTL_PTR_MASK 0x3f00
#define MV88E6393X_PORT_POLICY_MGMT_CTL_DATA_MASK 0x00ff
#define MV88E6393X_PORT_POLICY_MGMT_CTL_PTR_01C280000000XLO 0x2000
#define MV88E6393X_PORT_POLICY_MGMT_CTL_PTR_01C280000000XHI 0x2100
#define MV88E6393X_PORT_POLICY_MGMT_CTL_PTR_01C280000002XLO 0x2400
#define MV88E6393X_PORT_POLICY_MGMT_CTL_PTR_01C280000002XHI 0x2500
#define MV88E6393X_PORT_POLICY_MGMT_CTL_PTR_INGRESS_DEST 0x3000
#define MV88E6393X_PORT_POLICY_MGMT_CTL_PTR_CPU_DEST 0x3800
#define MV88E6393X_PORT_POLICY_MGMT_CTL_CPU_DEST_MGMTPRI 0x00e0
/* Offset 0x0F: Port Special Ether Type */ /* Offset 0x0F: Port Special Ether Type */
#define MV88E6XXX_PORT_ETH_TYPE 0x0f #define MV88E6XXX_PORT_ETH_TYPE 0x0f
#define MV88E6XXX_PORT_ETH_TYPE_DEFAULT 0x9100 #define MV88E6XXX_PORT_ETH_TYPE_DEFAULT 0x9100
...@@ -243,6 +264,15 @@ ...@@ -243,6 +264,15 @@
/* Offset 0x10: InDiscards Low Counter */ /* Offset 0x10: InDiscards Low Counter */
#define MV88E6XXX_PORT_IN_DISCARD_LO 0x10 #define MV88E6XXX_PORT_IN_DISCARD_LO 0x10
/* Offset 0x10: Extended Port Control Command */
#define MV88E6393X_PORT_EPC_CMD 0x10
#define MV88E6393X_PORT_EPC_CMD_BUSY 0x8000
#define MV88E6393X_PORT_EPC_CMD_WRITE 0x0300
#define MV88E6393X_PORT_EPC_INDEX_PORT_ETYPE 0x02
/* Offset 0x11: Extended Port Control Data */
#define MV88E6393X_PORT_EPC_DATA 0x11
/* Offset 0x11: InDiscards High Counter */ /* Offset 0x11: InDiscards High Counter */
#define MV88E6XXX_PORT_IN_DISCARD_HI 0x11 #define MV88E6XXX_PORT_IN_DISCARD_HI 0x11
...@@ -288,6 +318,8 @@ int mv88e6xxx_port_read(struct mv88e6xxx_chip *chip, int port, int reg, ...@@ -288,6 +318,8 @@ int mv88e6xxx_port_read(struct mv88e6xxx_chip *chip, int port, int reg,
u16 *val); u16 *val);
int mv88e6xxx_port_write(struct mv88e6xxx_chip *chip, int port, int reg, int mv88e6xxx_port_write(struct mv88e6xxx_chip *chip, int port, int reg,
u16 val); u16 val);
int mv88e6xxx_port_wait_bit(struct mv88e6xxx_chip *chip, int port, int reg,
int bit, int val);
int mv88e6185_port_set_pause(struct mv88e6xxx_chip *chip, int port, int mv88e6185_port_set_pause(struct mv88e6xxx_chip *chip, int port,
int pause); int pause);
...@@ -315,10 +347,13 @@ int mv88e6390_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port, ...@@ -315,10 +347,13 @@ int mv88e6390_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port,
int speed, int duplex); int speed, int duplex);
int mv88e6390x_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port, int mv88e6390x_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port,
int speed, int duplex); int speed, int duplex);
int mv88e6393x_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port,
int speed, int duplex);
phy_interface_t mv88e6341_port_max_speed_mode(int port); phy_interface_t mv88e6341_port_max_speed_mode(int port);
phy_interface_t mv88e6390_port_max_speed_mode(int port); phy_interface_t mv88e6390_port_max_speed_mode(int port);
phy_interface_t mv88e6390x_port_max_speed_mode(int port); phy_interface_t mv88e6390x_port_max_speed_mode(int port);
phy_interface_t mv88e6393x_port_max_speed_mode(int port);
int mv88e6xxx_port_set_state(struct mv88e6xxx_chip *chip, int port, u8 state); int mv88e6xxx_port_set_state(struct mv88e6xxx_chip *chip, int port, u8 state);
...@@ -351,8 +386,19 @@ int mv88e6352_port_set_mcast_flood(struct mv88e6xxx_chip *chip, int port, ...@@ -351,8 +386,19 @@ int mv88e6352_port_set_mcast_flood(struct mv88e6xxx_chip *chip, int port,
int mv88e6352_port_set_policy(struct mv88e6xxx_chip *chip, int port, int mv88e6352_port_set_policy(struct mv88e6xxx_chip *chip, int port,
enum mv88e6xxx_policy_mapping mapping, enum mv88e6xxx_policy_mapping mapping,
enum mv88e6xxx_policy_action action); enum mv88e6xxx_policy_action action);
int mv88e6393x_port_set_policy(struct mv88e6xxx_chip *chip, int port,
enum mv88e6xxx_policy_mapping mapping,
enum mv88e6xxx_policy_action action);
int mv88e6351_port_set_ether_type(struct mv88e6xxx_chip *chip, int port, int mv88e6351_port_set_ether_type(struct mv88e6xxx_chip *chip, int port,
u16 etype); u16 etype);
int mv88e6393x_set_egress_port(struct mv88e6xxx_chip *chip,
enum mv88e6xxx_egress_direction direction,
int port);
int mv88e6393x_port_set_upstream_port(struct mv88e6xxx_chip *chip, int port,
int upstream_port);
int mv88e6393x_port_mgmt_rsvd2cpu(struct mv88e6xxx_chip *chip);
int mv88e6393x_port_set_ether_type(struct mv88e6xxx_chip *chip, int port,
u16 etype);
int mv88e6xxx_port_set_message_port(struct mv88e6xxx_chip *chip, int port, int mv88e6xxx_port_set_message_port(struct mv88e6xxx_chip *chip, int port,
bool message_port); bool message_port);
int mv88e6xxx_port_set_trunk(struct mv88e6xxx_chip *chip, int port, int mv88e6xxx_port_set_trunk(struct mv88e6xxx_chip *chip, int port,
...@@ -371,6 +417,8 @@ int mv88e6390_port_set_cmode(struct mv88e6xxx_chip *chip, int port, ...@@ -371,6 +417,8 @@ int mv88e6390_port_set_cmode(struct mv88e6xxx_chip *chip, int port,
phy_interface_t mode); phy_interface_t mode);
int mv88e6390x_port_set_cmode(struct mv88e6xxx_chip *chip, int port, int mv88e6390x_port_set_cmode(struct mv88e6xxx_chip *chip, int port,
phy_interface_t mode); phy_interface_t mode);
int mv88e6393x_port_set_cmode(struct mv88e6xxx_chip *chip, int port,
phy_interface_t mode);
int mv88e6185_port_get_cmode(struct mv88e6xxx_chip *chip, int port, u8 *cmode); int mv88e6185_port_get_cmode(struct mv88e6xxx_chip *chip, int port, u8 *cmode);
int mv88e6352_port_get_cmode(struct mv88e6xxx_chip *chip, int port, u8 *cmode); int mv88e6352_port_get_cmode(struct mv88e6xxx_chip *chip, int port, u8 *cmode);
int mv88e6xxx_port_set_map_da(struct mv88e6xxx_chip *chip, int port); int mv88e6xxx_port_set_map_da(struct mv88e6xxx_chip *chip, int port);
......
...@@ -95,7 +95,7 @@ static int mv88e6xxx_serdes_pcs_get_state(struct mv88e6xxx_chip *chip, ...@@ -95,7 +95,7 @@ static int mv88e6xxx_serdes_pcs_get_state(struct mv88e6xxx_chip *chip,
return 0; return 0;
} }
int mv88e6352_serdes_power(struct mv88e6xxx_chip *chip, int port, u8 lane, int mv88e6352_serdes_power(struct mv88e6xxx_chip *chip, int port, int lane,
bool up) bool up)
{ {
u16 val, new_val; u16 val, new_val;
...@@ -117,7 +117,7 @@ int mv88e6352_serdes_power(struct mv88e6xxx_chip *chip, int port, u8 lane, ...@@ -117,7 +117,7 @@ int mv88e6352_serdes_power(struct mv88e6xxx_chip *chip, int port, u8 lane,
} }
int mv88e6352_serdes_pcs_config(struct mv88e6xxx_chip *chip, int port, int mv88e6352_serdes_pcs_config(struct mv88e6xxx_chip *chip, int port,
u8 lane, unsigned int mode, int lane, unsigned int mode,
phy_interface_t interface, phy_interface_t interface,
const unsigned long *advertise) const unsigned long *advertise)
{ {
...@@ -166,7 +166,7 @@ int mv88e6352_serdes_pcs_config(struct mv88e6xxx_chip *chip, int port, ...@@ -166,7 +166,7 @@ int mv88e6352_serdes_pcs_config(struct mv88e6xxx_chip *chip, int port,
} }
int mv88e6352_serdes_pcs_get_state(struct mv88e6xxx_chip *chip, int port, int mv88e6352_serdes_pcs_get_state(struct mv88e6xxx_chip *chip, int port,
u8 lane, struct phylink_link_state *state) int lane, struct phylink_link_state *state)
{ {
u16 lpa, status; u16 lpa, status;
int err; int err;
...@@ -187,7 +187,7 @@ int mv88e6352_serdes_pcs_get_state(struct mv88e6xxx_chip *chip, int port, ...@@ -187,7 +187,7 @@ int mv88e6352_serdes_pcs_get_state(struct mv88e6xxx_chip *chip, int port,
} }
int mv88e6352_serdes_pcs_an_restart(struct mv88e6xxx_chip *chip, int port, int mv88e6352_serdes_pcs_an_restart(struct mv88e6xxx_chip *chip, int port,
u8 lane) int lane)
{ {
u16 bmcr; u16 bmcr;
int err; int err;
...@@ -200,7 +200,7 @@ int mv88e6352_serdes_pcs_an_restart(struct mv88e6xxx_chip *chip, int port, ...@@ -200,7 +200,7 @@ int mv88e6352_serdes_pcs_an_restart(struct mv88e6xxx_chip *chip, int port,
} }
int mv88e6352_serdes_pcs_link_up(struct mv88e6xxx_chip *chip, int port, int mv88e6352_serdes_pcs_link_up(struct mv88e6xxx_chip *chip, int port,
u8 lane, int speed, int duplex) int lane, int speed, int duplex)
{ {
u16 val, bmcr; u16 val, bmcr;
int err; int err;
...@@ -230,10 +230,10 @@ int mv88e6352_serdes_pcs_link_up(struct mv88e6xxx_chip *chip, int port, ...@@ -230,10 +230,10 @@ int mv88e6352_serdes_pcs_link_up(struct mv88e6xxx_chip *chip, int port,
return mv88e6352_serdes_write(chip, MII_BMCR, bmcr); return mv88e6352_serdes_write(chip, MII_BMCR, bmcr);
} }
u8 mv88e6352_serdes_get_lane(struct mv88e6xxx_chip *chip, int port) int mv88e6352_serdes_get_lane(struct mv88e6xxx_chip *chip, int port)
{ {
u8 cmode = chip->ports[port].cmode; u8 cmode = chip->ports[port].cmode;
u8 lane = 0; int lane = -ENODEV;
if ((cmode == MV88E6XXX_PORT_STS_CMODE_100BASEX) || if ((cmode == MV88E6XXX_PORT_STS_CMODE_100BASEX) ||
(cmode == MV88E6XXX_PORT_STS_CMODE_1000BASEX) || (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASEX) ||
...@@ -245,7 +245,7 @@ u8 mv88e6352_serdes_get_lane(struct mv88e6xxx_chip *chip, int port) ...@@ -245,7 +245,7 @@ u8 mv88e6352_serdes_get_lane(struct mv88e6xxx_chip *chip, int port)
static bool mv88e6352_port_has_serdes(struct mv88e6xxx_chip *chip, int port) static bool mv88e6352_port_has_serdes(struct mv88e6xxx_chip *chip, int port)
{ {
if (mv88e6xxx_serdes_get_lane(chip, port)) if (mv88e6xxx_serdes_get_lane(chip, port) >= 0)
return true; return true;
return false; return false;
...@@ -354,7 +354,7 @@ static void mv88e6352_serdes_irq_link(struct mv88e6xxx_chip *chip, int port) ...@@ -354,7 +354,7 @@ static void mv88e6352_serdes_irq_link(struct mv88e6xxx_chip *chip, int port)
} }
irqreturn_t mv88e6352_serdes_irq_status(struct mv88e6xxx_chip *chip, int port, irqreturn_t mv88e6352_serdes_irq_status(struct mv88e6xxx_chip *chip, int port,
u8 lane) int lane)
{ {
irqreturn_t ret = IRQ_NONE; irqreturn_t ret = IRQ_NONE;
u16 status; u16 status;
...@@ -372,7 +372,7 @@ irqreturn_t mv88e6352_serdes_irq_status(struct mv88e6xxx_chip *chip, int port, ...@@ -372,7 +372,7 @@ irqreturn_t mv88e6352_serdes_irq_status(struct mv88e6xxx_chip *chip, int port,
return ret; return ret;
} }
int mv88e6352_serdes_irq_enable(struct mv88e6xxx_chip *chip, int port, u8 lane, int mv88e6352_serdes_irq_enable(struct mv88e6xxx_chip *chip, int port, int lane,
bool enable) bool enable)
{ {
u16 val = 0; u16 val = 0;
...@@ -413,10 +413,10 @@ void mv88e6352_serdes_get_regs(struct mv88e6xxx_chip *chip, int port, void *_p) ...@@ -413,10 +413,10 @@ void mv88e6352_serdes_get_regs(struct mv88e6xxx_chip *chip, int port, void *_p)
} }
} }
u8 mv88e6341_serdes_get_lane(struct mv88e6xxx_chip *chip, int port) int mv88e6341_serdes_get_lane(struct mv88e6xxx_chip *chip, int port)
{ {
u8 cmode = chip->ports[port].cmode; u8 cmode = chip->ports[port].cmode;
u8 lane = 0; int lane = -ENODEV;
switch (port) { switch (port) {
case 5: case 5:
...@@ -430,7 +430,7 @@ u8 mv88e6341_serdes_get_lane(struct mv88e6xxx_chip *chip, int port) ...@@ -430,7 +430,7 @@ u8 mv88e6341_serdes_get_lane(struct mv88e6xxx_chip *chip, int port)
return lane; return lane;
} }
int mv88e6185_serdes_power(struct mv88e6xxx_chip *chip, int port, u8 lane, int mv88e6185_serdes_power(struct mv88e6xxx_chip *chip, int port, int lane,
bool up) bool up)
{ {
/* The serdes power can't be controlled on this switch chip but we need /* The serdes power can't be controlled on this switch chip but we need
...@@ -440,7 +440,7 @@ int mv88e6185_serdes_power(struct mv88e6xxx_chip *chip, int port, u8 lane, ...@@ -440,7 +440,7 @@ int mv88e6185_serdes_power(struct mv88e6xxx_chip *chip, int port, u8 lane,
return 0; return 0;
} }
u8 mv88e6185_serdes_get_lane(struct mv88e6xxx_chip *chip, int port) int mv88e6185_serdes_get_lane(struct mv88e6xxx_chip *chip, int port)
{ {
/* There are no configurable serdes lanes on this switch chip but we /* There are no configurable serdes lanes on this switch chip but we
* need to return non-zero so that callers of * need to return non-zero so that callers of
...@@ -456,7 +456,7 @@ u8 mv88e6185_serdes_get_lane(struct mv88e6xxx_chip *chip, int port) ...@@ -456,7 +456,7 @@ u8 mv88e6185_serdes_get_lane(struct mv88e6xxx_chip *chip, int port)
} }
int mv88e6185_serdes_pcs_get_state(struct mv88e6xxx_chip *chip, int port, int mv88e6185_serdes_pcs_get_state(struct mv88e6xxx_chip *chip, int port,
u8 lane, struct phylink_link_state *state) int lane, struct phylink_link_state *state)
{ {
int err; int err;
u16 status; u16 status;
...@@ -492,7 +492,7 @@ int mv88e6185_serdes_pcs_get_state(struct mv88e6xxx_chip *chip, int port, ...@@ -492,7 +492,7 @@ int mv88e6185_serdes_pcs_get_state(struct mv88e6xxx_chip *chip, int port,
return 0; return 0;
} }
int mv88e6097_serdes_irq_enable(struct mv88e6xxx_chip *chip, int port, u8 lane, int mv88e6097_serdes_irq_enable(struct mv88e6xxx_chip *chip, int port, int lane,
bool enable) bool enable)
{ {
u8 cmode = chip->ports[port].cmode; u8 cmode = chip->ports[port].cmode;
...@@ -525,7 +525,7 @@ static void mv88e6097_serdes_irq_link(struct mv88e6xxx_chip *chip, int port) ...@@ -525,7 +525,7 @@ static void mv88e6097_serdes_irq_link(struct mv88e6xxx_chip *chip, int port)
} }
irqreturn_t mv88e6097_serdes_irq_status(struct mv88e6xxx_chip *chip, int port, irqreturn_t mv88e6097_serdes_irq_status(struct mv88e6xxx_chip *chip, int port,
u8 lane) int lane)
{ {
u8 cmode = chip->ports[port].cmode; u8 cmode = chip->ports[port].cmode;
...@@ -539,10 +539,10 @@ irqreturn_t mv88e6097_serdes_irq_status(struct mv88e6xxx_chip *chip, int port, ...@@ -539,10 +539,10 @@ irqreturn_t mv88e6097_serdes_irq_status(struct mv88e6xxx_chip *chip, int port,
return IRQ_NONE; return IRQ_NONE;
} }
u8 mv88e6390_serdes_get_lane(struct mv88e6xxx_chip *chip, int port) int mv88e6390_serdes_get_lane(struct mv88e6xxx_chip *chip, int port)
{ {
u8 cmode = chip->ports[port].cmode; u8 cmode = chip->ports[port].cmode;
u8 lane = 0; int lane = -ENODEV;
switch (port) { switch (port) {
case 9: case 9:
...@@ -562,12 +562,12 @@ u8 mv88e6390_serdes_get_lane(struct mv88e6xxx_chip *chip, int port) ...@@ -562,12 +562,12 @@ u8 mv88e6390_serdes_get_lane(struct mv88e6xxx_chip *chip, int port)
return lane; return lane;
} }
u8 mv88e6390x_serdes_get_lane(struct mv88e6xxx_chip *chip, int port) int mv88e6390x_serdes_get_lane(struct mv88e6xxx_chip *chip, int port)
{ {
u8 cmode_port = chip->ports[port].cmode; u8 cmode_port = chip->ports[port].cmode;
u8 cmode_port10 = chip->ports[10].cmode; u8 cmode_port10 = chip->ports[10].cmode;
u8 cmode_port9 = chip->ports[9].cmode; u8 cmode_port9 = chip->ports[9].cmode;
u8 lane = 0; int lane = -ENODEV;
switch (port) { switch (port) {
case 2: case 2:
...@@ -637,8 +637,29 @@ u8 mv88e6390x_serdes_get_lane(struct mv88e6xxx_chip *chip, int port) ...@@ -637,8 +637,29 @@ u8 mv88e6390x_serdes_get_lane(struct mv88e6xxx_chip *chip, int port)
return lane; return lane;
} }
/* Only Ports 0, 9 and 10 have SERDES lanes. Return the SERDES lane address
* a port is using else Returns -ENODEV.
*/
int mv88e6393x_serdes_get_lane(struct mv88e6xxx_chip *chip, int port)
{
u8 cmode = chip->ports[port].cmode;
int lane = -ENODEV;
if (port != 0 && port != 9 && port != 10)
return -EOPNOTSUPP;
if (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASEX ||
cmode == MV88E6XXX_PORT_STS_CMODE_SGMII ||
cmode == MV88E6XXX_PORT_STS_CMODE_2500BASEX ||
cmode == MV88E6393X_PORT_STS_CMODE_5GBASER ||
cmode == MV88E6393X_PORT_STS_CMODE_10GBASER)
lane = port;
return lane;
}
/* Set power up/down for 10GBASE-R and 10GBASE-X4/X2 */ /* Set power up/down for 10GBASE-R and 10GBASE-X4/X2 */
static int mv88e6390_serdes_power_10g(struct mv88e6xxx_chip *chip, u8 lane, static int mv88e6390_serdes_power_10g(struct mv88e6xxx_chip *chip, int lane,
bool up) bool up)
{ {
u16 val, new_val; u16 val, new_val;
...@@ -665,7 +686,7 @@ static int mv88e6390_serdes_power_10g(struct mv88e6xxx_chip *chip, u8 lane, ...@@ -665,7 +686,7 @@ static int mv88e6390_serdes_power_10g(struct mv88e6xxx_chip *chip, u8 lane,
} }
/* Set power up/down for SGMII and 1000Base-X */ /* Set power up/down for SGMII and 1000Base-X */
static int mv88e6390_serdes_power_sgmii(struct mv88e6xxx_chip *chip, u8 lane, static int mv88e6390_serdes_power_sgmii(struct mv88e6xxx_chip *chip, int lane,
bool up) bool up)
{ {
u16 val, new_val; u16 val, new_val;
...@@ -701,7 +722,7 @@ static struct mv88e6390_serdes_hw_stat mv88e6390_serdes_hw_stats[] = { ...@@ -701,7 +722,7 @@ static struct mv88e6390_serdes_hw_stat mv88e6390_serdes_hw_stats[] = {
int mv88e6390_serdes_get_sset_count(struct mv88e6xxx_chip *chip, int port) int mv88e6390_serdes_get_sset_count(struct mv88e6xxx_chip *chip, int port)
{ {
if (mv88e6390_serdes_get_lane(chip, port) == 0) if (mv88e6390_serdes_get_lane(chip, port) < 0)
return 0; return 0;
return ARRAY_SIZE(mv88e6390_serdes_hw_stats); return ARRAY_SIZE(mv88e6390_serdes_hw_stats);
...@@ -713,7 +734,7 @@ int mv88e6390_serdes_get_strings(struct mv88e6xxx_chip *chip, ...@@ -713,7 +734,7 @@ int mv88e6390_serdes_get_strings(struct mv88e6xxx_chip *chip,
struct mv88e6390_serdes_hw_stat *stat; struct mv88e6390_serdes_hw_stat *stat;
int i; int i;
if (mv88e6390_serdes_get_lane(chip, port) == 0) if (mv88e6390_serdes_get_lane(chip, port) < 0)
return 0; return 0;
for (i = 0; i < ARRAY_SIZE(mv88e6390_serdes_hw_stats); i++) { for (i = 0; i < ARRAY_SIZE(mv88e6390_serdes_hw_stats); i++) {
...@@ -750,7 +771,7 @@ int mv88e6390_serdes_get_stats(struct mv88e6xxx_chip *chip, int port, ...@@ -750,7 +771,7 @@ int mv88e6390_serdes_get_stats(struct mv88e6xxx_chip *chip, int port,
int i; int i;
lane = mv88e6390_serdes_get_lane(chip, port); lane = mv88e6390_serdes_get_lane(chip, port);
if (lane == 0) if (lane < 0)
return 0; return 0;
for (i = 0; i < ARRAY_SIZE(mv88e6390_serdes_hw_stats); i++) { for (i = 0; i < ARRAY_SIZE(mv88e6390_serdes_hw_stats); i++) {
...@@ -761,7 +782,7 @@ int mv88e6390_serdes_get_stats(struct mv88e6xxx_chip *chip, int port, ...@@ -761,7 +782,7 @@ int mv88e6390_serdes_get_stats(struct mv88e6xxx_chip *chip, int port,
return ARRAY_SIZE(mv88e6390_serdes_hw_stats); return ARRAY_SIZE(mv88e6390_serdes_hw_stats);
} }
static int mv88e6390_serdes_enable_checker(struct mv88e6xxx_chip *chip, u8 lane) static int mv88e6390_serdes_enable_checker(struct mv88e6xxx_chip *chip, int lane)
{ {
u16 reg; u16 reg;
int err; int err;
...@@ -776,7 +797,7 @@ static int mv88e6390_serdes_enable_checker(struct mv88e6xxx_chip *chip, u8 lane) ...@@ -776,7 +797,7 @@ static int mv88e6390_serdes_enable_checker(struct mv88e6xxx_chip *chip, u8 lane)
MV88E6390_PG_CONTROL, reg); MV88E6390_PG_CONTROL, reg);
} }
int mv88e6390_serdes_power(struct mv88e6xxx_chip *chip, int port, u8 lane, int mv88e6390_serdes_power(struct mv88e6xxx_chip *chip, int port, int lane,
bool up) bool up)
{ {
u8 cmode = chip->ports[port].cmode; u8 cmode = chip->ports[port].cmode;
...@@ -801,7 +822,7 @@ int mv88e6390_serdes_power(struct mv88e6xxx_chip *chip, int port, u8 lane, ...@@ -801,7 +822,7 @@ int mv88e6390_serdes_power(struct mv88e6xxx_chip *chip, int port, u8 lane,
} }
int mv88e6390_serdes_pcs_config(struct mv88e6xxx_chip *chip, int port, int mv88e6390_serdes_pcs_config(struct mv88e6xxx_chip *chip, int port,
u8 lane, unsigned int mode, int lane, unsigned int mode,
phy_interface_t interface, phy_interface_t interface,
const unsigned long *advertise) const unsigned long *advertise)
{ {
...@@ -860,7 +881,7 @@ int mv88e6390_serdes_pcs_config(struct mv88e6xxx_chip *chip, int port, ...@@ -860,7 +881,7 @@ int mv88e6390_serdes_pcs_config(struct mv88e6xxx_chip *chip, int port,
} }
static int mv88e6390_serdes_pcs_get_state_sgmii(struct mv88e6xxx_chip *chip, static int mv88e6390_serdes_pcs_get_state_sgmii(struct mv88e6xxx_chip *chip,
int port, u8 lane, struct phylink_link_state *state) int port, int lane, struct phylink_link_state *state)
{ {
u16 lpa, status; u16 lpa, status;
int err; int err;
...@@ -883,7 +904,7 @@ static int mv88e6390_serdes_pcs_get_state_sgmii(struct mv88e6xxx_chip *chip, ...@@ -883,7 +904,7 @@ static int mv88e6390_serdes_pcs_get_state_sgmii(struct mv88e6xxx_chip *chip,
} }
static int mv88e6390_serdes_pcs_get_state_10g(struct mv88e6xxx_chip *chip, static int mv88e6390_serdes_pcs_get_state_10g(struct mv88e6xxx_chip *chip,
int port, u8 lane, struct phylink_link_state *state) int port, int lane, struct phylink_link_state *state)
{ {
u16 status; u16 status;
int err; int err;
...@@ -902,8 +923,32 @@ static int mv88e6390_serdes_pcs_get_state_10g(struct mv88e6xxx_chip *chip, ...@@ -902,8 +923,32 @@ static int mv88e6390_serdes_pcs_get_state_10g(struct mv88e6xxx_chip *chip,
return 0; return 0;
} }
static int mv88e6393x_serdes_pcs_get_state_10g(struct mv88e6xxx_chip *chip,
int port, int lane,
struct phylink_link_state *state)
{
u16 status;
int err;
err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
MV88E6390_10G_STAT1, &status);
if (err)
return err;
state->link = !!(status & MDIO_STAT1_LSTATUS);
if (state->link) {
if (state->interface == PHY_INTERFACE_MODE_5GBASER)
state->speed = SPEED_5000;
else
state->speed = SPEED_10000;
state->duplex = DUPLEX_FULL;
}
return 0;
}
int mv88e6390_serdes_pcs_get_state(struct mv88e6xxx_chip *chip, int port, int mv88e6390_serdes_pcs_get_state(struct mv88e6xxx_chip *chip, int port,
u8 lane, struct phylink_link_state *state) int lane, struct phylink_link_state *state)
{ {
switch (state->interface) { switch (state->interface) {
case PHY_INTERFACE_MODE_SGMII: case PHY_INTERFACE_MODE_SGMII:
...@@ -921,8 +966,27 @@ int mv88e6390_serdes_pcs_get_state(struct mv88e6xxx_chip *chip, int port, ...@@ -921,8 +966,27 @@ int mv88e6390_serdes_pcs_get_state(struct mv88e6xxx_chip *chip, int port,
} }
} }
int mv88e6393x_serdes_pcs_get_state(struct mv88e6xxx_chip *chip, int port,
int lane, struct phylink_link_state *state)
{
switch (state->interface) {
case PHY_INTERFACE_MODE_SGMII:
case PHY_INTERFACE_MODE_1000BASEX:
case PHY_INTERFACE_MODE_2500BASEX:
return mv88e6390_serdes_pcs_get_state_sgmii(chip, port, lane,
state);
case PHY_INTERFACE_MODE_5GBASER:
case PHY_INTERFACE_MODE_10GBASER:
return mv88e6393x_serdes_pcs_get_state_10g(chip, port, lane,
state);
default:
return -EOPNOTSUPP;
}
}
int mv88e6390_serdes_pcs_an_restart(struct mv88e6xxx_chip *chip, int port, int mv88e6390_serdes_pcs_an_restart(struct mv88e6xxx_chip *chip, int port,
u8 lane) int lane)
{ {
u16 bmcr; u16 bmcr;
int err; int err;
...@@ -938,7 +1002,7 @@ int mv88e6390_serdes_pcs_an_restart(struct mv88e6xxx_chip *chip, int port, ...@@ -938,7 +1002,7 @@ int mv88e6390_serdes_pcs_an_restart(struct mv88e6xxx_chip *chip, int port,
} }
int mv88e6390_serdes_pcs_link_up(struct mv88e6xxx_chip *chip, int port, int mv88e6390_serdes_pcs_link_up(struct mv88e6xxx_chip *chip, int port,
u8 lane, int speed, int duplex) int lane, int speed, int duplex)
{ {
u16 val, bmcr; u16 val, bmcr;
int err; int err;
...@@ -972,7 +1036,7 @@ int mv88e6390_serdes_pcs_link_up(struct mv88e6xxx_chip *chip, int port, ...@@ -972,7 +1036,7 @@ int mv88e6390_serdes_pcs_link_up(struct mv88e6xxx_chip *chip, int port,
} }
static void mv88e6390_serdes_irq_link_sgmii(struct mv88e6xxx_chip *chip, static void mv88e6390_serdes_irq_link_sgmii(struct mv88e6xxx_chip *chip,
int port, u8 lane) int port, int lane)
{ {
u16 bmsr; u16 bmsr;
int err; int err;
...@@ -988,8 +1052,25 @@ static void mv88e6390_serdes_irq_link_sgmii(struct mv88e6xxx_chip *chip, ...@@ -988,8 +1052,25 @@ static void mv88e6390_serdes_irq_link_sgmii(struct mv88e6xxx_chip *chip,
dsa_port_phylink_mac_change(chip->ds, port, !!(bmsr & BMSR_LSTATUS)); dsa_port_phylink_mac_change(chip->ds, port, !!(bmsr & BMSR_LSTATUS));
} }
static void mv88e6393x_serdes_irq_link_10g(struct mv88e6xxx_chip *chip,
int port, u8 lane)
{
u16 status;
int err;
/* If the link has dropped, we want to know about it. */
err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
MV88E6390_10G_STAT1, &status);
if (err) {
dev_err(chip->dev, "can't read Serdes STAT1: %d\n", err);
return;
}
dsa_port_phylink_mac_change(chip->ds, port, !!(status & MDIO_STAT1_LSTATUS));
}
static int mv88e6390_serdes_irq_enable_sgmii(struct mv88e6xxx_chip *chip, static int mv88e6390_serdes_irq_enable_sgmii(struct mv88e6xxx_chip *chip,
u8 lane, bool enable) int lane, bool enable)
{ {
u16 val = 0; u16 val = 0;
...@@ -1001,7 +1082,7 @@ static int mv88e6390_serdes_irq_enable_sgmii(struct mv88e6xxx_chip *chip, ...@@ -1001,7 +1082,7 @@ static int mv88e6390_serdes_irq_enable_sgmii(struct mv88e6xxx_chip *chip,
MV88E6390_SGMII_INT_ENABLE, val); MV88E6390_SGMII_INT_ENABLE, val);
} }
int mv88e6390_serdes_irq_enable(struct mv88e6xxx_chip *chip, int port, u8 lane, int mv88e6390_serdes_irq_enable(struct mv88e6xxx_chip *chip, int port, int lane,
bool enable) bool enable)
{ {
u8 cmode = chip->ports[port].cmode; u8 cmode = chip->ports[port].cmode;
...@@ -1017,7 +1098,7 @@ int mv88e6390_serdes_irq_enable(struct mv88e6xxx_chip *chip, int port, u8 lane, ...@@ -1017,7 +1098,7 @@ int mv88e6390_serdes_irq_enable(struct mv88e6xxx_chip *chip, int port, u8 lane,
} }
static int mv88e6390_serdes_irq_status_sgmii(struct mv88e6xxx_chip *chip, static int mv88e6390_serdes_irq_status_sgmii(struct mv88e6xxx_chip *chip,
u8 lane, u16 *status) int lane, u16 *status)
{ {
int err; int err;
...@@ -1027,8 +1108,85 @@ static int mv88e6390_serdes_irq_status_sgmii(struct mv88e6xxx_chip *chip, ...@@ -1027,8 +1108,85 @@ static int mv88e6390_serdes_irq_status_sgmii(struct mv88e6xxx_chip *chip,
return err; return err;
} }
static int mv88e6393x_serdes_irq_enable_10g(struct mv88e6xxx_chip *chip,
u8 lane, bool enable)
{
u16 val = 0;
if (enable)
val |= MV88E6393X_10G_INT_LINK_CHANGE;
return mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS,
MV88E6393X_10G_INT_ENABLE, val);
}
int mv88e6393x_serdes_irq_enable(struct mv88e6xxx_chip *chip, int port,
int lane, bool enable)
{
u8 cmode = chip->ports[port].cmode;
switch (cmode) {
case MV88E6XXX_PORT_STS_CMODE_SGMII:
case MV88E6XXX_PORT_STS_CMODE_1000BASEX:
case MV88E6XXX_PORT_STS_CMODE_2500BASEX:
return mv88e6390_serdes_irq_enable_sgmii(chip, lane, enable);
case MV88E6393X_PORT_STS_CMODE_5GBASER:
case MV88E6393X_PORT_STS_CMODE_10GBASER:
return mv88e6393x_serdes_irq_enable_10g(chip, lane, enable);
}
return 0;
}
static int mv88e6393x_serdes_irq_status_10g(struct mv88e6xxx_chip *chip,
u8 lane, u16 *status)
{
int err;
err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
MV88E6393X_10G_INT_STATUS, status);
return err;
}
irqreturn_t mv88e6393x_serdes_irq_status(struct mv88e6xxx_chip *chip, int port,
int lane)
{
u8 cmode = chip->ports[port].cmode;
irqreturn_t ret = IRQ_NONE;
u16 status;
int err;
switch (cmode) {
case MV88E6XXX_PORT_STS_CMODE_SGMII:
case MV88E6XXX_PORT_STS_CMODE_1000BASEX:
case MV88E6XXX_PORT_STS_CMODE_2500BASEX:
err = mv88e6390_serdes_irq_status_sgmii(chip, lane, &status);
if (err)
return ret;
if (status & (MV88E6390_SGMII_INT_LINK_DOWN |
MV88E6390_SGMII_INT_LINK_UP)) {
ret = IRQ_HANDLED;
mv88e6390_serdes_irq_link_sgmii(chip, port, lane);
}
break;
case MV88E6393X_PORT_STS_CMODE_5GBASER:
case MV88E6393X_PORT_STS_CMODE_10GBASER:
err = mv88e6393x_serdes_irq_status_10g(chip, lane, &status);
if (err)
return err;
if (status & MV88E6393X_10G_INT_LINK_CHANGE) {
ret = IRQ_HANDLED;
mv88e6393x_serdes_irq_link_10g(chip, port, lane);
}
break;
}
return ret;
}
irqreturn_t mv88e6390_serdes_irq_status(struct mv88e6xxx_chip *chip, int port, irqreturn_t mv88e6390_serdes_irq_status(struct mv88e6xxx_chip *chip, int port,
u8 lane) int lane)
{ {
u8 cmode = chip->ports[port].cmode; u8 cmode = chip->ports[port].cmode;
irqreturn_t ret = IRQ_NONE; irqreturn_t ret = IRQ_NONE;
...@@ -1087,7 +1245,7 @@ static const u16 mv88e6390_serdes_regs[] = { ...@@ -1087,7 +1245,7 @@ static const u16 mv88e6390_serdes_regs[] = {
int mv88e6390_serdes_get_regs_len(struct mv88e6xxx_chip *chip, int port) int mv88e6390_serdes_get_regs_len(struct mv88e6xxx_chip *chip, int port)
{ {
if (mv88e6xxx_serdes_get_lane(chip, port) == 0) if (mv88e6xxx_serdes_get_lane(chip, port) < 0)
return 0; return 0;
return ARRAY_SIZE(mv88e6390_serdes_regs) * sizeof(u16); return ARRAY_SIZE(mv88e6390_serdes_regs) * sizeof(u16);
...@@ -1102,7 +1260,7 @@ void mv88e6390_serdes_get_regs(struct mv88e6xxx_chip *chip, int port, void *_p) ...@@ -1102,7 +1260,7 @@ void mv88e6390_serdes_get_regs(struct mv88e6xxx_chip *chip, int port, void *_p)
int i; int i;
lane = mv88e6xxx_serdes_get_lane(chip, port); lane = mv88e6xxx_serdes_get_lane(chip, port);
if (lane == 0) if (lane < 0)
return; return;
for (i = 0 ; i < ARRAY_SIZE(mv88e6390_serdes_regs); i++) { for (i = 0 ; i < ARRAY_SIZE(mv88e6390_serdes_regs); i++) {
...@@ -1112,3 +1270,101 @@ void mv88e6390_serdes_get_regs(struct mv88e6xxx_chip *chip, int port, void *_p) ...@@ -1112,3 +1270,101 @@ void mv88e6390_serdes_get_regs(struct mv88e6xxx_chip *chip, int port, void *_p)
p[i] = reg; p[i] = reg;
} }
} }
static int mv88e6393x_serdes_port_errata(struct mv88e6xxx_chip *chip, int lane)
{
u16 reg, pcs;
int err;
/* mv88e6393x family errata 4.6:
* Cannot clear PwrDn bit on SERDES on port 0 if device is configured
* CPU_MGD mode or P0_mode is configured for [x]MII.
* Workaround: Set Port0 SERDES register 4.F002 bit 5=0 and bit 15=1.
*
* It seems that after this workaround the SERDES is automatically
* powered up (the bit is cleared), so power it down.
*/
if (lane == MV88E6393X_PORT0_LANE) {
err = mv88e6390_serdes_read(chip, MV88E6393X_PORT0_LANE,
MDIO_MMD_PHYXS,
MV88E6393X_SERDES_POC, &reg);
if (err)
return err;
reg &= ~MV88E6393X_SERDES_POC_PDOWN;
reg |= MV88E6393X_SERDES_POC_RESET;
err = mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS,
MV88E6393X_SERDES_POC, reg);
if (err)
return err;
err = mv88e6390_serdes_power_sgmii(chip, lane, false);
if (err)
return err;
}
/* mv88e6393x family errata 4.8:
* When a SERDES port is operating in 1000BASE-X or SGMII mode link may
* not come up after hardware reset or software reset of SERDES core.
* Workaround is to write SERDES register 4.F074.14=1 for only those
* modes and 0 in all other modes.
*/
err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
MV88E6393X_SERDES_POC, &pcs);
if (err)
return err;
pcs &= MV88E6393X_SERDES_POC_PCS_MASK;
err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
MV88E6393X_ERRATA_4_8_REG, &reg);
if (err)
return err;
if (pcs == MV88E6393X_SERDES_POC_PCS_1000BASEX ||
pcs == MV88E6393X_SERDES_POC_PCS_SGMII_PHY ||
pcs == MV88E6393X_SERDES_POC_PCS_SGMII_MAC)
reg |= MV88E6393X_ERRATA_4_8_BIT;
else
reg &= ~MV88E6393X_ERRATA_4_8_BIT;
return mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS,
MV88E6393X_ERRATA_4_8_REG, reg);
}
int mv88e6393x_serdes_setup_errata(struct mv88e6xxx_chip *chip)
{
int err;
err = mv88e6393x_serdes_port_errata(chip, MV88E6393X_PORT0_LANE);
if (err)
return err;
err = mv88e6393x_serdes_port_errata(chip, MV88E6393X_PORT9_LANE);
if (err)
return err;
return mv88e6393x_serdes_port_errata(chip, MV88E6393X_PORT10_LANE);
}
int mv88e6393x_serdes_power(struct mv88e6xxx_chip *chip, int port, int lane,
bool on)
{
u8 cmode = chip->ports[port].cmode;
if (port != 0 && port != 9 && port != 10)
return -EOPNOTSUPP;
switch (cmode) {
case MV88E6XXX_PORT_STS_CMODE_SGMII:
case MV88E6XXX_PORT_STS_CMODE_1000BASEX:
case MV88E6XXX_PORT_STS_CMODE_2500BASEX:
return mv88e6390_serdes_power_sgmii(chip, lane, on);
case MV88E6393X_PORT_STS_CMODE_5GBASER:
case MV88E6393X_PORT_STS_CMODE_10GBASER:
return mv88e6390_serdes_power_10g(chip, lane, on);
}
return 0;
}
...@@ -42,6 +42,9 @@ ...@@ -42,6 +42,9 @@
/* 10GBASE-R and 10GBASE-X4/X2 */ /* 10GBASE-R and 10GBASE-X4/X2 */
#define MV88E6390_10G_CTRL1 (0x1000 + MDIO_CTRL1) #define MV88E6390_10G_CTRL1 (0x1000 + MDIO_CTRL1)
#define MV88E6390_10G_STAT1 (0x1000 + MDIO_STAT1) #define MV88E6390_10G_STAT1 (0x1000 + MDIO_STAT1)
#define MV88E6393X_10G_INT_ENABLE 0x9000
#define MV88E6393X_10G_INT_LINK_CHANGE BIT(2)
#define MV88E6393X_10G_INT_STATUS 0x9001
/* 1000BASE-X and SGMII */ /* 1000BASE-X and SGMII */
#define MV88E6390_SGMII_BMCR (0x2000 + MII_BMCR) #define MV88E6390_SGMII_BMCR (0x2000 + MII_BMCR)
...@@ -73,55 +76,86 @@ ...@@ -73,55 +76,86 @@
#define MV88E6390_PG_CONTROL 0xf010 #define MV88E6390_PG_CONTROL 0xf010
#define MV88E6390_PG_CONTROL_ENABLE_PC BIT(0) #define MV88E6390_PG_CONTROL_ENABLE_PC BIT(0)
u8 mv88e6185_serdes_get_lane(struct mv88e6xxx_chip *chip, int port); #define MV88E6393X_PORT0_LANE 0x00
u8 mv88e6341_serdes_get_lane(struct mv88e6xxx_chip *chip, int port); #define MV88E6393X_PORT9_LANE 0x09
u8 mv88e6352_serdes_get_lane(struct mv88e6xxx_chip *chip, int port); #define MV88E6393X_PORT10_LANE 0x0a
u8 mv88e6390_serdes_get_lane(struct mv88e6xxx_chip *chip, int port);
u8 mv88e6390x_serdes_get_lane(struct mv88e6xxx_chip *chip, int port); /* Port Operational Configuration */
#define MV88E6393X_SERDES_POC 0xf002
#define MV88E6393X_SERDES_POC_PCS_1000BASEX 0x0000
#define MV88E6393X_SERDES_POC_PCS_2500BASEX 0x0001
#define MV88E6393X_SERDES_POC_PCS_SGMII_PHY 0x0002
#define MV88E6393X_SERDES_POC_PCS_SGMII_MAC 0x0003
#define MV88E6393X_SERDES_POC_PCS_5GBASER 0x0004
#define MV88E6393X_SERDES_POC_PCS_10GBASER 0x0005
#define MV88E6393X_SERDES_POC_PCS_USXGMII_PHY 0x0006
#define MV88E6393X_SERDES_POC_PCS_USXGMII_MAC 0x0007
#define MV88E6393X_SERDES_POC_PCS_MASK 0x0007
#define MV88E6393X_SERDES_POC_RESET BIT(15)
#define MV88E6393X_SERDES_POC_PDOWN BIT(5)
#define MV88E6393X_ERRATA_4_8_REG 0xF074
#define MV88E6393X_ERRATA_4_8_BIT BIT(14)
int mv88e6185_serdes_get_lane(struct mv88e6xxx_chip *chip, int port);
int mv88e6341_serdes_get_lane(struct mv88e6xxx_chip *chip, int port);
int mv88e6352_serdes_get_lane(struct mv88e6xxx_chip *chip, int port);
int mv88e6390_serdes_get_lane(struct mv88e6xxx_chip *chip, int port);
int mv88e6390x_serdes_get_lane(struct mv88e6xxx_chip *chip, int port);
int mv88e6393x_serdes_get_lane(struct mv88e6xxx_chip *chip, int port);
int mv88e6352_serdes_pcs_config(struct mv88e6xxx_chip *chip, int port, int mv88e6352_serdes_pcs_config(struct mv88e6xxx_chip *chip, int port,
u8 lane, unsigned int mode, int lane, unsigned int mode,
phy_interface_t interface, phy_interface_t interface,
const unsigned long *advertise); const unsigned long *advertise);
int mv88e6390_serdes_pcs_config(struct mv88e6xxx_chip *chip, int port, int mv88e6390_serdes_pcs_config(struct mv88e6xxx_chip *chip, int port,
u8 lane, unsigned int mode, int lane, unsigned int mode,
phy_interface_t interface, phy_interface_t interface,
const unsigned long *advertise); const unsigned long *advertise);
int mv88e6185_serdes_pcs_get_state(struct mv88e6xxx_chip *chip, int port, int mv88e6185_serdes_pcs_get_state(struct mv88e6xxx_chip *chip, int port,
u8 lane, struct phylink_link_state *state); int lane, struct phylink_link_state *state);
int mv88e6352_serdes_pcs_get_state(struct mv88e6xxx_chip *chip, int port, int mv88e6352_serdes_pcs_get_state(struct mv88e6xxx_chip *chip, int port,
u8 lane, struct phylink_link_state *state); int lane, struct phylink_link_state *state);
int mv88e6390_serdes_pcs_get_state(struct mv88e6xxx_chip *chip, int port, int mv88e6390_serdes_pcs_get_state(struct mv88e6xxx_chip *chip, int port,
u8 lane, struct phylink_link_state *state); int lane, struct phylink_link_state *state);
int mv88e6393x_serdes_pcs_get_state(struct mv88e6xxx_chip *chip, int port,
int lane, struct phylink_link_state *state);
int mv88e6352_serdes_pcs_an_restart(struct mv88e6xxx_chip *chip, int port, int mv88e6352_serdes_pcs_an_restart(struct mv88e6xxx_chip *chip, int port,
u8 lane); int lane);
int mv88e6390_serdes_pcs_an_restart(struct mv88e6xxx_chip *chip, int port, int mv88e6390_serdes_pcs_an_restart(struct mv88e6xxx_chip *chip, int port,
u8 lane); int lane);
int mv88e6352_serdes_pcs_link_up(struct mv88e6xxx_chip *chip, int port, int mv88e6352_serdes_pcs_link_up(struct mv88e6xxx_chip *chip, int port,
u8 lane, int speed, int duplex); int lane, int speed, int duplex);
int mv88e6390_serdes_pcs_link_up(struct mv88e6xxx_chip *chip, int port, int mv88e6390_serdes_pcs_link_up(struct mv88e6xxx_chip *chip, int port,
u8 lane, int speed, int duplex); int lane, int speed, int duplex);
unsigned int mv88e6352_serdes_irq_mapping(struct mv88e6xxx_chip *chip, unsigned int mv88e6352_serdes_irq_mapping(struct mv88e6xxx_chip *chip,
int port); int port);
unsigned int mv88e6390_serdes_irq_mapping(struct mv88e6xxx_chip *chip, unsigned int mv88e6390_serdes_irq_mapping(struct mv88e6xxx_chip *chip,
int port); int port);
int mv88e6185_serdes_power(struct mv88e6xxx_chip *chip, int port, u8 lane, int mv88e6185_serdes_power(struct mv88e6xxx_chip *chip, int port, int lane,
bool up); bool up);
int mv88e6352_serdes_power(struct mv88e6xxx_chip *chip, int port, u8 lane, int mv88e6352_serdes_power(struct mv88e6xxx_chip *chip, int port, int lane,
bool on); bool on);
int mv88e6390_serdes_power(struct mv88e6xxx_chip *chip, int port, u8 lane, int mv88e6390_serdes_power(struct mv88e6xxx_chip *chip, int port, int lane,
bool on); bool on);
int mv88e6097_serdes_irq_enable(struct mv88e6xxx_chip *chip, int port, u8 lane, int mv88e6393x_serdes_power(struct mv88e6xxx_chip *chip, int port, int lane,
bool on);
int mv88e6393x_serdes_setup_errata(struct mv88e6xxx_chip *chip);
int mv88e6097_serdes_irq_enable(struct mv88e6xxx_chip *chip, int port, int lane,
bool enable); bool enable);
int mv88e6352_serdes_irq_enable(struct mv88e6xxx_chip *chip, int port, u8 lane, int mv88e6352_serdes_irq_enable(struct mv88e6xxx_chip *chip, int port, int lane,
bool enable); bool enable);
int mv88e6390_serdes_irq_enable(struct mv88e6xxx_chip *chip, int port, u8 lane, int mv88e6390_serdes_irq_enable(struct mv88e6xxx_chip *chip, int port, int lane,
bool enable); bool enable);
int mv88e6393x_serdes_irq_enable(struct mv88e6xxx_chip *chip, int port,
int lane, bool enable);
irqreturn_t mv88e6097_serdes_irq_status(struct mv88e6xxx_chip *chip, int port, irqreturn_t mv88e6097_serdes_irq_status(struct mv88e6xxx_chip *chip, int port,
u8 lane); int lane);
irqreturn_t mv88e6352_serdes_irq_status(struct mv88e6xxx_chip *chip, int port, irqreturn_t mv88e6352_serdes_irq_status(struct mv88e6xxx_chip *chip, int port,
u8 lane); int lane);
irqreturn_t mv88e6390_serdes_irq_status(struct mv88e6xxx_chip *chip, int port, irqreturn_t mv88e6390_serdes_irq_status(struct mv88e6xxx_chip *chip, int port,
u8 lane); int lane);
irqreturn_t mv88e6393x_serdes_irq_status(struct mv88e6xxx_chip *chip, int port,
int lane);
int mv88e6352_serdes_get_sset_count(struct mv88e6xxx_chip *chip, int port); int mv88e6352_serdes_get_sset_count(struct mv88e6xxx_chip *chip, int port);
int mv88e6352_serdes_get_strings(struct mv88e6xxx_chip *chip, int mv88e6352_serdes_get_strings(struct mv88e6xxx_chip *chip,
int port, uint8_t *data); int port, uint8_t *data);
...@@ -138,18 +172,18 @@ void mv88e6352_serdes_get_regs(struct mv88e6xxx_chip *chip, int port, void *_p); ...@@ -138,18 +172,18 @@ 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); 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); void mv88e6390_serdes_get_regs(struct mv88e6xxx_chip *chip, int port, void *_p);
/* Return the (first) SERDES lane address a port is using, 0 otherwise. */ /* Return the (first) SERDES lane address a port is using, -errno otherwise. */
static inline u8 mv88e6xxx_serdes_get_lane(struct mv88e6xxx_chip *chip, static inline int mv88e6xxx_serdes_get_lane(struct mv88e6xxx_chip *chip,
int port) int port)
{ {
if (!chip->info->ops->serdes_get_lane) if (!chip->info->ops->serdes_get_lane)
return 0; return -EOPNOTSUPP;
return chip->info->ops->serdes_get_lane(chip, port); return chip->info->ops->serdes_get_lane(chip, port);
} }
static inline int mv88e6xxx_serdes_power_up(struct mv88e6xxx_chip *chip, static inline int mv88e6xxx_serdes_power_up(struct mv88e6xxx_chip *chip,
int port, u8 lane) int port, int lane)
{ {
if (!chip->info->ops->serdes_power) if (!chip->info->ops->serdes_power)
return -EOPNOTSUPP; return -EOPNOTSUPP;
...@@ -158,7 +192,7 @@ static inline int mv88e6xxx_serdes_power_up(struct mv88e6xxx_chip *chip, ...@@ -158,7 +192,7 @@ static inline int mv88e6xxx_serdes_power_up(struct mv88e6xxx_chip *chip,
} }
static inline int mv88e6xxx_serdes_power_down(struct mv88e6xxx_chip *chip, static inline int mv88e6xxx_serdes_power_down(struct mv88e6xxx_chip *chip,
int port, u8 lane) int port, int lane)
{ {
if (!chip->info->ops->serdes_power) if (!chip->info->ops->serdes_power)
return -EOPNOTSUPP; return -EOPNOTSUPP;
...@@ -176,7 +210,7 @@ mv88e6xxx_serdes_irq_mapping(struct mv88e6xxx_chip *chip, int port) ...@@ -176,7 +210,7 @@ mv88e6xxx_serdes_irq_mapping(struct mv88e6xxx_chip *chip, int port)
} }
static inline int mv88e6xxx_serdes_irq_enable(struct mv88e6xxx_chip *chip, static inline int mv88e6xxx_serdes_irq_enable(struct mv88e6xxx_chip *chip,
int port, u8 lane) int port, int lane)
{ {
if (!chip->info->ops->serdes_irq_enable) if (!chip->info->ops->serdes_irq_enable)
return -EOPNOTSUPP; return -EOPNOTSUPP;
...@@ -185,7 +219,7 @@ static inline int mv88e6xxx_serdes_irq_enable(struct mv88e6xxx_chip *chip, ...@@ -185,7 +219,7 @@ static inline int mv88e6xxx_serdes_irq_enable(struct mv88e6xxx_chip *chip,
} }
static inline int mv88e6xxx_serdes_irq_disable(struct mv88e6xxx_chip *chip, static inline int mv88e6xxx_serdes_irq_disable(struct mv88e6xxx_chip *chip,
int port, u8 lane) int port, int lane)
{ {
if (!chip->info->ops->serdes_irq_enable) if (!chip->info->ops->serdes_irq_enable)
return -EOPNOTSUPP; return -EOPNOTSUPP;
...@@ -194,7 +228,7 @@ static inline int mv88e6xxx_serdes_irq_disable(struct mv88e6xxx_chip *chip, ...@@ -194,7 +228,7 @@ static inline int mv88e6xxx_serdes_irq_disable(struct mv88e6xxx_chip *chip,
} }
static inline irqreturn_t static inline irqreturn_t
mv88e6xxx_serdes_irq_status(struct mv88e6xxx_chip *chip, int port, u8 lane) mv88e6xxx_serdes_irq_status(struct mv88e6xxx_chip *chip, int port, int lane)
{ {
if (!chip->info->ops->serdes_irq_status) if (!chip->info->ops->serdes_irq_status)
return IRQ_NONE; return IRQ_NONE;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment