2016-11-01 03:21:49

by Vivien Didelot

[permalink] [raw]
Subject: [PATCH net-next 00/11] net: dsa: mv88e6xxx: refine port operations

The Marvell chips have one internal SMI device per port, containing a
set of registers used to configure a port's link, STP state, default
VLAN or addresses database, etc.

This patchset creates port files to implement the port operations as
described in datasheets, and extend the chip ops structure with them.

Patches 1 to 6 implement accessors for port's STP state, port based VLAN
map, default FID, default VID, and 802.1Q mode.

Patches 7 to 11 implement the port's MAC setup of link state, duplex
mode, RGMII delay and speed, all accessed through port's register 0x01.

The new port's MAC setup code is used to re-implement the adjust_link
code and correctly force the link down before changing any of the MAC
settings, as requested by the datasheets.

The port's MAC accessors use values compatible with struct phy_device
(e.g. DUPLEX_FULL) and extend them when needed (e.g. SPEED_MAX).

Vivien Didelot (11):
net: dsa: mv88e6xxx: add port files
net: dsa: mv88e6xxx: add port state setter
net: dsa: mv88e6xxx: add port vlan map setter
net: dsa: mv88e6xxx: add port FID accessors
net: dsa: mv88e6xxx: add port PVID accessors
net: dsa: mv88e6xxx: add port 802.1Q mode setter
net: dsa: mv88e6xxx: add port link setter
net: dsa: mv88e6xxx: add port duplex setter
net: dsa: mv88e6xxx: add port's RGMII delay setter
net: dsa: mv88e6xxx: add port's MAC speed setter
net: dsa: mv88e6xxx: setup port's MAC

drivers/net/dsa/mv88e6xxx/Makefile | 1 +
drivers/net/dsa/mv88e6xxx/chip.c | 434 +++++++++---------------------
drivers/net/dsa/mv88e6xxx/mv88e6xxx.h | 44 ++-
drivers/net/dsa/mv88e6xxx/port.c | 487 ++++++++++++++++++++++++++++++++++
drivers/net/dsa/mv88e6xxx/port.h | 52 ++++
5 files changed, 710 insertions(+), 308 deletions(-)
create mode 100644 drivers/net/dsa/mv88e6xxx/port.c
create mode 100644 drivers/net/dsa/mv88e6xxx/port.h

--
2.10.2


2016-11-01 03:21:53

by Vivien Didelot

[permalink] [raw]
Subject: [PATCH net-next 01/11] net: dsa: mv88e6xxx: add port files

The Marvell switches contains one internal SMI device per port, called
"Port Registers". Depending on the model, the addresses of these devices
start from 0x0, 0x8 or 0x10.

Start moving Port Registers specific code to their own files.

Signed-off-by: Vivien Didelot <[email protected]>
---
drivers/net/dsa/mv88e6xxx/Makefile | 1 +
drivers/net/dsa/mv88e6xxx/chip.c | 17 +----------------
drivers/net/dsa/mv88e6xxx/port.c | 31 +++++++++++++++++++++++++++++++
drivers/net/dsa/mv88e6xxx/port.h | 24 ++++++++++++++++++++++++
4 files changed, 57 insertions(+), 16 deletions(-)
create mode 100644 drivers/net/dsa/mv88e6xxx/port.c
create mode 100644 drivers/net/dsa/mv88e6xxx/port.h

diff --git a/drivers/net/dsa/mv88e6xxx/Makefile b/drivers/net/dsa/mv88e6xxx/Makefile
index 10ce820..c36be31 100644
--- a/drivers/net/dsa/mv88e6xxx/Makefile
+++ b/drivers/net/dsa/mv88e6xxx/Makefile
@@ -2,3 +2,4 @@ obj-$(CONFIG_NET_DSA_MV88E6XXX) += mv88e6xxx.o
mv88e6xxx-objs := chip.o
mv88e6xxx-objs += global1.o
mv88e6xxx-$(CONFIG_NET_DSA_MV88E6XXX_GLOBAL2) += global2.o
+mv88e6xxx-objs += port.o
diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c
index 72b9dac2..b32b242 100644
--- a/drivers/net/dsa/mv88e6xxx/chip.c
+++ b/drivers/net/dsa/mv88e6xxx/chip.c
@@ -37,6 +37,7 @@
#include "mv88e6xxx.h"
#include "global1.h"
#include "global2.h"
+#include "port.h"

static void assert_reg_lock(struct mv88e6xxx_chip *chip)
{
@@ -221,22 +222,6 @@ int mv88e6xxx_write(struct mv88e6xxx_chip *chip, int addr, int reg, u16 val)
return 0;
}

-static int mv88e6xxx_port_read(struct mv88e6xxx_chip *chip, int port, int reg,
- u16 *val)
-{
- int addr = chip->info->port_base_addr + port;
-
- return mv88e6xxx_read(chip, addr, reg, val);
-}
-
-static int mv88e6xxx_port_write(struct mv88e6xxx_chip *chip, int port, int reg,
- u16 val)
-{
- int addr = chip->info->port_base_addr + port;
-
- return mv88e6xxx_write(chip, addr, reg, val);
-}
-
static int mv88e6xxx_phy_read(struct mv88e6xxx_chip *chip, int phy,
int reg, u16 *val)
{
diff --git a/drivers/net/dsa/mv88e6xxx/port.c b/drivers/net/dsa/mv88e6xxx/port.c
new file mode 100644
index 0000000..3b36c92
--- /dev/null
+++ b/drivers/net/dsa/mv88e6xxx/port.c
@@ -0,0 +1,31 @@
+/*
+ * Marvell 88E6xxx Switch Port Registers support
+ *
+ * Copyright (c) 2008 Marvell Semiconductor
+ *
+ * Copyright (c) 2016 Vivien Didelot <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include "mv88e6xxx.h"
+#include "port.h"
+
+int mv88e6xxx_port_read(struct mv88e6xxx_chip *chip, int port, int reg,
+ u16 *val)
+{
+ int addr = chip->info->port_base_addr + port;
+
+ return mv88e6xxx_read(chip, addr, reg, val);
+}
+
+int mv88e6xxx_port_write(struct mv88e6xxx_chip *chip, int port, int reg,
+ u16 val)
+{
+ int addr = chip->info->port_base_addr + port;
+
+ return mv88e6xxx_write(chip, addr, reg, val);
+}
diff --git a/drivers/net/dsa/mv88e6xxx/port.h b/drivers/net/dsa/mv88e6xxx/port.h
new file mode 100644
index 0000000..ae1ae2b
--- /dev/null
+++ b/drivers/net/dsa/mv88e6xxx/port.h
@@ -0,0 +1,24 @@
+/*
+ * Marvell 88E6xxx Switch Port Registers support
+ *
+ * Copyright (c) 2008 Marvell Semiconductor
+ *
+ * Copyright (c) 2016 Vivien Didelot <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef _MV88E6XXX_PORT_H
+#define _MV88E6XXX_PORT_H
+
+#include "mv88e6xxx.h"
+
+int mv88e6xxx_port_read(struct mv88e6xxx_chip *chip, int port, int reg,
+ u16 *val);
+int mv88e6xxx_port_write(struct mv88e6xxx_chip *chip, int port, int reg,
+ u16 val);
+
+#endif /* _MV88E6XXX_PORT_H */
--
2.10.2

2016-11-01 03:23:48

by Vivien Didelot

[permalink] [raw]
Subject: [PATCH net-next 09/11] net: dsa: mv88e6xxx: add port's RGMII delay setter

Some chips such as 88E6352 and 88E6390 can be programmed to add delays
to RXCLK for IND inputs or to GTXCLK for OUTD outputs when port is in
RGMII mode.

Add a port function to program such delays according to the provided PHY
interface mode.

Signed-off-by: Vivien Didelot <[email protected]>
---
drivers/net/dsa/mv88e6xxx/chip.c | 4 +++
drivers/net/dsa/mv88e6xxx/mv88e6xxx.h | 6 ++++
drivers/net/dsa/mv88e6xxx/port.c | 58 +++++++++++++++++++++++++++++++++++
drivers/net/dsa/mv88e6xxx/port.h | 5 +++
4 files changed, 73 insertions(+)

diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c
index 49a6935..bb93d0a 100644
--- a/drivers/net/dsa/mv88e6xxx/chip.c
+++ b/drivers/net/dsa/mv88e6xxx/chip.c
@@ -3220,6 +3220,7 @@ static const struct mv88e6xxx_ops mv88e6172_ops = {
.phy_write = mv88e6xxx_g2_smi_phy_write,
.port_set_link = mv88e6xxx_port_set_link,
.port_set_duplex = mv88e6xxx_port_set_duplex,
+ .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay,
};

static const struct mv88e6xxx_ops mv88e6175_ops = {
@@ -3238,6 +3239,7 @@ static const struct mv88e6xxx_ops mv88e6176_ops = {
.phy_write = mv88e6xxx_g2_smi_phy_write,
.port_set_link = mv88e6xxx_port_set_link,
.port_set_duplex = mv88e6xxx_port_set_duplex,
+ .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay,
};

static const struct mv88e6xxx_ops mv88e6185_ops = {
@@ -3256,6 +3258,7 @@ static const struct mv88e6xxx_ops mv88e6240_ops = {
.phy_write = mv88e6xxx_g2_smi_phy_write,
.port_set_link = mv88e6xxx_port_set_link,
.port_set_duplex = mv88e6xxx_port_set_duplex,
+ .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay,
};

static const struct mv88e6xxx_ops mv88e6320_ops = {
@@ -3302,6 +3305,7 @@ static const struct mv88e6xxx_ops mv88e6352_ops = {
.phy_write = mv88e6xxx_g2_smi_phy_write,
.port_set_link = mv88e6xxx_port_set_link,
.port_set_duplex = mv88e6xxx_port_set_duplex,
+ .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay,
};

static const struct mv88e6xxx_info mv88e6xxx_table[] = {
diff --git a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h
index 35420ed..0d3223b 100644
--- a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h
+++ b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h
@@ -728,6 +728,12 @@ struct mv88e6xxx_ops {
int (*phy_write)(struct mv88e6xxx_chip *chip, int addr, int reg,
u16 val);

+ /* RGMII Receive/Transmit Timing Control
+ * Add delay on PHY_INTERFACE_MODE_RGMII_*ID, no delay otherwise.
+ */
+ int (*port_set_rgmii_delay)(struct mv88e6xxx_chip *chip, int port,
+ phy_interface_t mode);
+
#define LINK_UNKNOWN -1

/* Port's MAC link state
diff --git a/drivers/net/dsa/mv88e6xxx/port.c b/drivers/net/dsa/mv88e6xxx/port.c
index 386db46..ef6efc9 100644
--- a/drivers/net/dsa/mv88e6xxx/port.c
+++ b/drivers/net/dsa/mv88e6xxx/port.c
@@ -35,6 +35,64 @@ int mv88e6xxx_port_write(struct mv88e6xxx_chip *chip, int port, int reg,
* Link, Duplex and Flow Control have one force bit, one value bit.
*/

+static int mv88e6xxx_port_set_rgmii_delay(struct mv88e6xxx_chip *chip, int port,
+ phy_interface_t mode)
+{
+ u16 reg;
+ int err;
+
+ err = mv88e6xxx_port_read(chip, port, PORT_PCS_CTRL, &reg);
+ if (err)
+ return err;
+
+ reg &= ~(PORT_PCS_CTRL_RGMII_DELAY_RXCLK |
+ PORT_PCS_CTRL_RGMII_DELAY_TXCLK);
+
+ switch (mode) {
+ case PHY_INTERFACE_MODE_RGMII_RXID:
+ reg |= PORT_PCS_CTRL_RGMII_DELAY_RXCLK;
+ break;
+ case PHY_INTERFACE_MODE_RGMII_TXID:
+ reg |= PORT_PCS_CTRL_RGMII_DELAY_TXCLK;
+ break;
+ case PHY_INTERFACE_MODE_RGMII_ID:
+ reg |= PORT_PCS_CTRL_RGMII_DELAY_RXCLK |
+ PORT_PCS_CTRL_RGMII_DELAY_TXCLK;
+ break;
+ default:
+ /* no delay */
+ break;
+ }
+
+ err = mv88e6xxx_port_write(chip, port, PORT_PCS_CTRL, reg);
+ if (err)
+ return err;
+
+ netdev_dbg(chip->ds->ports[port].netdev, "delay RXCLK %s, TXCLK %s\n",
+ reg & PORT_PCS_CTRL_RGMII_DELAY_RXCLK ? "yes" : "no",
+ reg & PORT_PCS_CTRL_RGMII_DELAY_TXCLK ? "yes" : "no");
+
+ return 0;
+}
+
+int mv88e6352_port_set_rgmii_delay(struct mv88e6xxx_chip *chip, int port,
+ phy_interface_t mode)
+{
+ if (port < 5)
+ return -EOPNOTSUPP;
+
+ return mv88e6xxx_port_set_rgmii_delay(chip, port, mode);
+}
+
+int mv88e6390_port_set_rgmii_delay(struct mv88e6xxx_chip *chip, int port,
+ phy_interface_t mode)
+{
+ if (port != 0)
+ return -EOPNOTSUPP;
+
+ return mv88e6xxx_port_set_rgmii_delay(chip, port, mode);
+}
+
int mv88e6xxx_port_set_link(struct mv88e6xxx_chip *chip, int port, int link)
{
u16 reg;
diff --git a/drivers/net/dsa/mv88e6xxx/port.h b/drivers/net/dsa/mv88e6xxx/port.h
index f73e90e..3472b79 100644
--- a/drivers/net/dsa/mv88e6xxx/port.h
+++ b/drivers/net/dsa/mv88e6xxx/port.h
@@ -21,6 +21,11 @@ int mv88e6xxx_port_read(struct mv88e6xxx_chip *chip, int port, int reg,
int mv88e6xxx_port_write(struct mv88e6xxx_chip *chip, int port, int reg,
u16 val);

+int mv88e6352_port_set_rgmii_delay(struct mv88e6xxx_chip *chip, int port,
+ phy_interface_t mode);
+int mv88e6390_port_set_rgmii_delay(struct mv88e6xxx_chip *chip, int port,
+ phy_interface_t mode);
+
int mv88e6xxx_port_set_link(struct mv88e6xxx_chip *chip, int port, int link);

int mv88e6xxx_port_set_duplex(struct mv88e6xxx_chip *chip, int port, int dup);
--
2.10.2

2016-11-01 03:23:50

by Vivien Didelot

[permalink] [raw]
Subject: [PATCH net-next 10/11] net: dsa: mv88e6xxx: add port's MAC speed setter

While the two bits for link, duplex or RGMII delays are used the same
way on chips supporting the said feature, the two bits for speed have
different meaning for most of the chips out there.

Speed value is stored in bits 1:0, 0x3 means unforce (normal detection).

Some chips reuse values for alternative speeds when bit 12 is set.

Newer chips with speed > 1Gbps reuse value 0x3 thus need a new bit 13.

Here are the values to write in register 0x1 to (un)force speed:

| Speed | 88E6065 | 88E6185 | 88E6352 | 88E6390 | 88E6390X |
| ------- | ------- | ------- | ------- | ------- | -------- |
| 10 | 0x0000 | 0x0000 | 0x0000 | 0x2000 | 0x2000 |
| 100 | 0x0001 | 0x0001 | 0x0001 | 0x2001 | 0x2001 |
| 200 | 0x0002 | NA | 0x1001 | 0x3001 | 0x3001 |
| 1000 | NA | 0x0002 | 0x0002 | 0x2002 | 0x2002 |
| 2500 | NA | NA | NA | 0x3003 | 0x3003 |
| 10000 | NA | NA | NA | NA | 0x2003 |
| unforce | 0x0003 | 0x0003 | 0x0003 | 0x0000 | 0x0000 |

This patch implements a generic mv88e6xxx_port_set_speed() function used
by chip-specific wrappers to filter supported ports and speeds.

Signed-off-by: Vivien Didelot <[email protected]>
---
drivers/net/dsa/mv88e6xxx/chip.c | 33 ++++++--
drivers/net/dsa/mv88e6xxx/mv88e6xxx.h | 23 +++++-
drivers/net/dsa/mv88e6xxx/port.c | 138 ++++++++++++++++++++++++++++++++++
drivers/net/dsa/mv88e6xxx/port.h | 6 ++
4 files changed, 188 insertions(+), 12 deletions(-)

diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c
index bb93d0a..23c05e3 100644
--- a/drivers/net/dsa/mv88e6xxx/chip.c
+++ b/drivers/net/dsa/mv88e6xxx/chip.c
@@ -725,7 +725,7 @@ static void mv88e6xxx_adjust_link(struct dsa_switch *ds, int port,
PORT_PCS_CTRL_FORCE_LINK |
PORT_PCS_CTRL_DUPLEX_FULL |
PORT_PCS_CTRL_FORCE_DUPLEX |
- PORT_PCS_CTRL_UNFORCED);
+ PORT_PCS_CTRL_SPEED_UNFORCED);

reg |= PORT_PCS_CTRL_FORCE_LINK;
if (phydev->link)
@@ -736,13 +736,13 @@ static void mv88e6xxx_adjust_link(struct dsa_switch *ds, int port,

switch (phydev->speed) {
case SPEED_1000:
- reg |= PORT_PCS_CTRL_1000;
+ reg |= PORT_PCS_CTRL_SPEED_1000;
break;
case SPEED_100:
- reg |= PORT_PCS_CTRL_100;
+ reg |= PORT_PCS_CTRL_SPEED_100;
break;
case SPEED_10:
- reg |= PORT_PCS_CTRL_10;
+ reg |= PORT_PCS_CTRL_SPEED_10;
break;
default:
pr_info("Unknown speed");
@@ -2421,17 +2421,17 @@ static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port)
*/
err = mv88e6xxx_port_read(chip, port, PORT_PCS_CTRL, &reg);
if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) {
- reg &= ~PORT_PCS_CTRL_UNFORCED;
+ reg &= ~PORT_PCS_CTRL_SPEED_UNFORCED;
reg |= PORT_PCS_CTRL_FORCE_LINK |
PORT_PCS_CTRL_LINK_UP |
PORT_PCS_CTRL_DUPLEX_FULL |
PORT_PCS_CTRL_FORCE_DUPLEX;
if (mv88e6xxx_6065_family(chip))
- reg |= PORT_PCS_CTRL_100;
+ reg |= PORT_PCS_CTRL_SPEED_100;
else
- reg |= PORT_PCS_CTRL_1000;
+ reg |= PORT_PCS_CTRL_SPEED_1000;
} else {
- reg |= PORT_PCS_CTRL_UNFORCED;
+ reg |= PORT_PCS_CTRL_SPEED_UNFORCED;
}

err = mv88e6xxx_port_write(chip, port, PORT_PCS_CTRL, reg);
@@ -3162,6 +3162,7 @@ static const struct mv88e6xxx_ops mv88e6085_ops = {
.phy_write = mv88e6xxx_phy_ppu_write,
.port_set_link = mv88e6xxx_port_set_link,
.port_set_duplex = mv88e6xxx_port_set_duplex,
+ .port_set_speed = mv88e6185_port_set_speed,
};

static const struct mv88e6xxx_ops mv88e6095_ops = {
@@ -3170,6 +3171,7 @@ static const struct mv88e6xxx_ops mv88e6095_ops = {
.phy_write = mv88e6xxx_phy_ppu_write,
.port_set_link = mv88e6xxx_port_set_link,
.port_set_duplex = mv88e6xxx_port_set_duplex,
+ .port_set_speed = mv88e6185_port_set_speed,
};

static const struct mv88e6xxx_ops mv88e6123_ops = {
@@ -3178,6 +3180,7 @@ static const struct mv88e6xxx_ops mv88e6123_ops = {
.phy_write = mv88e6xxx_write,
.port_set_link = mv88e6xxx_port_set_link,
.port_set_duplex = mv88e6xxx_port_set_duplex,
+ .port_set_speed = mv88e6185_port_set_speed,
};

static const struct mv88e6xxx_ops mv88e6131_ops = {
@@ -3186,6 +3189,7 @@ static const struct mv88e6xxx_ops mv88e6131_ops = {
.phy_write = mv88e6xxx_phy_ppu_write,
.port_set_link = mv88e6xxx_port_set_link,
.port_set_duplex = mv88e6xxx_port_set_duplex,
+ .port_set_speed = mv88e6185_port_set_speed,
};

static const struct mv88e6xxx_ops mv88e6161_ops = {
@@ -3194,6 +3198,7 @@ static const struct mv88e6xxx_ops mv88e6161_ops = {
.phy_write = mv88e6xxx_write,
.port_set_link = mv88e6xxx_port_set_link,
.port_set_duplex = mv88e6xxx_port_set_duplex,
+ .port_set_speed = mv88e6185_port_set_speed,
};

static const struct mv88e6xxx_ops mv88e6165_ops = {
@@ -3202,6 +3207,7 @@ static const struct mv88e6xxx_ops mv88e6165_ops = {
.phy_write = mv88e6xxx_write,
.port_set_link = mv88e6xxx_port_set_link,
.port_set_duplex = mv88e6xxx_port_set_duplex,
+ .port_set_speed = mv88e6185_port_set_speed,
};

static const struct mv88e6xxx_ops mv88e6171_ops = {
@@ -3210,6 +3216,7 @@ static const struct mv88e6xxx_ops mv88e6171_ops = {
.phy_write = mv88e6xxx_g2_smi_phy_write,
.port_set_link = mv88e6xxx_port_set_link,
.port_set_duplex = mv88e6xxx_port_set_duplex,
+ .port_set_speed = mv88e6185_port_set_speed,
};

static const struct mv88e6xxx_ops mv88e6172_ops = {
@@ -3221,6 +3228,7 @@ static const struct mv88e6xxx_ops mv88e6172_ops = {
.port_set_link = mv88e6xxx_port_set_link,
.port_set_duplex = mv88e6xxx_port_set_duplex,
.port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay,
+ .port_set_speed = mv88e6352_port_set_speed,
};

static const struct mv88e6xxx_ops mv88e6175_ops = {
@@ -3229,6 +3237,7 @@ static const struct mv88e6xxx_ops mv88e6175_ops = {
.phy_write = mv88e6xxx_g2_smi_phy_write,
.port_set_link = mv88e6xxx_port_set_link,
.port_set_duplex = mv88e6xxx_port_set_duplex,
+ .port_set_speed = mv88e6185_port_set_speed,
};

static const struct mv88e6xxx_ops mv88e6176_ops = {
@@ -3240,6 +3249,7 @@ static const struct mv88e6xxx_ops mv88e6176_ops = {
.port_set_link = mv88e6xxx_port_set_link,
.port_set_duplex = mv88e6xxx_port_set_duplex,
.port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay,
+ .port_set_speed = mv88e6352_port_set_speed,
};

static const struct mv88e6xxx_ops mv88e6185_ops = {
@@ -3248,6 +3258,7 @@ static const struct mv88e6xxx_ops mv88e6185_ops = {
.phy_write = mv88e6xxx_phy_ppu_write,
.port_set_link = mv88e6xxx_port_set_link,
.port_set_duplex = mv88e6xxx_port_set_duplex,
+ .port_set_speed = mv88e6185_port_set_speed,
};

static const struct mv88e6xxx_ops mv88e6240_ops = {
@@ -3259,6 +3270,7 @@ static const struct mv88e6xxx_ops mv88e6240_ops = {
.port_set_link = mv88e6xxx_port_set_link,
.port_set_duplex = mv88e6xxx_port_set_duplex,
.port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay,
+ .port_set_speed = mv88e6352_port_set_speed,
};

static const struct mv88e6xxx_ops mv88e6320_ops = {
@@ -3269,6 +3281,7 @@ static const struct mv88e6xxx_ops mv88e6320_ops = {
.phy_write = mv88e6xxx_g2_smi_phy_write,
.port_set_link = mv88e6xxx_port_set_link,
.port_set_duplex = mv88e6xxx_port_set_duplex,
+ .port_set_speed = mv88e6185_port_set_speed,
};

static const struct mv88e6xxx_ops mv88e6321_ops = {
@@ -3279,6 +3292,7 @@ static const struct mv88e6xxx_ops mv88e6321_ops = {
.phy_write = mv88e6xxx_g2_smi_phy_write,
.port_set_link = mv88e6xxx_port_set_link,
.port_set_duplex = mv88e6xxx_port_set_duplex,
+ .port_set_speed = mv88e6185_port_set_speed,
};

static const struct mv88e6xxx_ops mv88e6350_ops = {
@@ -3287,6 +3301,7 @@ static const struct mv88e6xxx_ops mv88e6350_ops = {
.phy_write = mv88e6xxx_g2_smi_phy_write,
.port_set_link = mv88e6xxx_port_set_link,
.port_set_duplex = mv88e6xxx_port_set_duplex,
+ .port_set_speed = mv88e6185_port_set_speed,
};

static const struct mv88e6xxx_ops mv88e6351_ops = {
@@ -3295,6 +3310,7 @@ static const struct mv88e6xxx_ops mv88e6351_ops = {
.phy_write = mv88e6xxx_g2_smi_phy_write,
.port_set_link = mv88e6xxx_port_set_link,
.port_set_duplex = mv88e6xxx_port_set_duplex,
+ .port_set_speed = mv88e6185_port_set_speed,
};

static const struct mv88e6xxx_ops mv88e6352_ops = {
@@ -3306,6 +3322,7 @@ static const struct mv88e6xxx_ops mv88e6352_ops = {
.port_set_link = mv88e6xxx_port_set_link,
.port_set_duplex = mv88e6xxx_port_set_duplex,
.port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay,
+ .port_set_speed = mv88e6352_port_set_speed,
};

static const struct mv88e6xxx_info mv88e6xxx_table[] = {
diff --git a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h
index 0d3223b..2bab24b 100644
--- a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h
+++ b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h
@@ -61,16 +61,22 @@
#define PORT_PCS_CTRL 0x01
#define PORT_PCS_CTRL_RGMII_DELAY_RXCLK BIT(15)
#define PORT_PCS_CTRL_RGMII_DELAY_TXCLK BIT(14)
+#define PORT_PCS_CTRL_FORCE_SPEED BIT(13) /* 6390 */
+#define PORT_PCS_CTRL_ALTSPEED BIT(12) /* 6390 */
+#define PORT_PCS_CTRL_200BASE BIT(12) /* 6352 */
#define PORT_PCS_CTRL_FC BIT(7)
#define PORT_PCS_CTRL_FORCE_FC BIT(6)
#define PORT_PCS_CTRL_LINK_UP BIT(5)
#define PORT_PCS_CTRL_FORCE_LINK BIT(4)
#define PORT_PCS_CTRL_DUPLEX_FULL BIT(3)
#define PORT_PCS_CTRL_FORCE_DUPLEX BIT(2)
-#define PORT_PCS_CTRL_10 0x00
-#define PORT_PCS_CTRL_100 0x01
-#define PORT_PCS_CTRL_1000 0x02
-#define PORT_PCS_CTRL_UNFORCED 0x03
+#define PORT_PCS_CTRL_SPEED_MASK (0x03)
+#define PORT_PCS_CTRL_SPEED_10 (0x00)
+#define PORT_PCS_CTRL_SPEED_100 (0x01)
+#define PORT_PCS_CTRL_SPEED_200 (0x02) /* 6065 and non Gb chips */
+#define PORT_PCS_CTRL_SPEED_1000 (0x02)
+#define PORT_PCS_CTRL_SPEED_10000 (0x03) /* 6390X */
+#define PORT_PCS_CTRL_SPEED_UNFORCED (0x03)
#define PORT_PAUSE_CTRL 0x02
#define PORT_SWITCH_ID 0x03
#define PORT_SWITCH_ID_PROD_NUM_6085 0x04a
@@ -748,6 +754,15 @@ struct mv88e6xxx_ops {
* DUPLEX_UNKNOWN for normal duplex detection.
*/
int (*port_set_duplex)(struct mv88e6xxx_chip *chip, int port, int dup);
+
+#define SPEED_MAX 0
+
+ /* Port's MAC speed (in Mbps)
+ *
+ * Depending on the chip, 10, 100, 200, 1000, 2500, 10000 are valid.
+ * Use SPEED_UNKNOWN for normal detection, SPEED_MAX for max value.
+ */
+ int (*port_set_speed)(struct mv88e6xxx_chip *chip, int port, int speed);
};

enum stat_type {
diff --git a/drivers/net/dsa/mv88e6xxx/port.c b/drivers/net/dsa/mv88e6xxx/port.c
index ef6efc9..6341559 100644
--- a/drivers/net/dsa/mv88e6xxx/port.c
+++ b/drivers/net/dsa/mv88e6xxx/port.c
@@ -33,6 +33,10 @@ int mv88e6xxx_port_write(struct mv88e6xxx_chip *chip, int port, int reg,
/* Offset 0x01: MAC (or PCS or Physical) Control Register
*
* Link, Duplex and Flow Control have one force bit, one value bit.
+ *
+ * For port's MAC speed, ForceSpd (or SpdValue) bits 1:0 program the value.
+ * Alternative values require the 200BASE (or AltSpeed) bit 12 set.
+ * Newer chips need a ForcedSpd bit 13 set to consider the value.
*/

static int mv88e6xxx_port_set_rgmii_delay(struct mv88e6xxx_chip *chip, int port,
@@ -155,6 +159,140 @@ int mv88e6xxx_port_set_duplex(struct mv88e6xxx_chip *chip, int port, int dup)
return 0;
}

+static int mv88e6xxx_port_set_speed(struct mv88e6xxx_chip *chip, int port,
+ int speed, bool alt_bit, bool force_bit)
+{
+ u16 reg, ctrl;
+ int err;
+
+ switch (speed) {
+ case 10:
+ ctrl = PORT_PCS_CTRL_SPEED_10;
+ break;
+ case 100:
+ ctrl = PORT_PCS_CTRL_SPEED_100;
+ break;
+ case 200:
+ if (alt_bit)
+ ctrl = PORT_PCS_CTRL_SPEED_100 | PORT_PCS_CTRL_ALTSPEED;
+ else
+ ctrl = PORT_PCS_CTRL_SPEED_200;
+ break;
+ case 1000:
+ ctrl = PORT_PCS_CTRL_SPEED_1000;
+ break;
+ case 2500:
+ ctrl = PORT_PCS_CTRL_SPEED_1000 | PORT_PCS_CTRL_ALTSPEED;
+ break;
+ case 10000:
+ /* all bits set, fall through... */
+ case SPEED_UNKNOWN:
+ ctrl = PORT_PCS_CTRL_SPEED_UNFORCED;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ err = mv88e6xxx_port_read(chip, port, PORT_PCS_CTRL, &reg);
+ if (err)
+ return err;
+
+ reg &= ~PORT_PCS_CTRL_SPEED_MASK;
+ if (alt_bit)
+ reg &= ~PORT_PCS_CTRL_ALTSPEED;
+ if (force_bit) {
+ reg &= ~PORT_PCS_CTRL_FORCE_SPEED;
+ if (speed)
+ ctrl |= PORT_PCS_CTRL_FORCE_SPEED;
+ }
+ reg |= ctrl;
+
+ err = mv88e6xxx_port_write(chip, port, PORT_PCS_CTRL, reg);
+ if (err)
+ return err;
+
+ if (speed)
+ netdev_dbg(chip->ds->ports[port].netdev,
+ "Speed set to %d Mbps\n", speed);
+ else
+ netdev_dbg(chip->ds->ports[port].netdev, "Speed unforced\n");
+
+ return 0;
+}
+
+/* Support 10, 100, 200 Mbps (e.g. 88E6065 family) */
+int mv88e6065_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed)
+{
+ if (!speed)
+ speed = 200;
+
+ if (speed > 200)
+ return -EOPNOTSUPP;
+
+ /* Setting 200 Mbps on port 0 to 3 selects 100 Mbps */
+ return mv88e6xxx_port_set_speed(chip, port, speed, false, false);
+}
+
+/* Support 10, 100, 1000 Mbps (e.g. 88E6185 family) */
+int mv88e6185_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed)
+{
+ if (!speed)
+ speed = 1000;
+
+ if (speed == 200 || speed > 1000)
+ return -EOPNOTSUPP;
+
+ return mv88e6xxx_port_set_speed(chip, port, speed, false, false);
+}
+
+/* Support 10, 100, 200, 1000 Mbps (e.g. 88E6352 family) */
+int mv88e6352_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed)
+{
+ if (!speed)
+ speed = 1000;
+
+ if (speed > 1000)
+ return -EOPNOTSUPP;
+
+ if (speed == 200 && port < 5)
+ return -EOPNOTSUPP;
+
+ return mv88e6xxx_port_set_speed(chip, port, speed, true, false);
+}
+
+/* Support 10, 100, 200, 1000, 2500 Mbps (e.g. 88E6390) */
+int mv88e6390_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed)
+{
+ if (!speed)
+ speed = port < 9 ? 1000 : 2500;
+
+ if (speed > 2500)
+ return -EOPNOTSUPP;
+
+ if (speed == 200 && port != 0)
+ return -EOPNOTSUPP;
+
+ if (speed == 2500 && port < 9)
+ return -EOPNOTSUPP;
+
+ return mv88e6xxx_port_set_speed(chip, port, speed, true, true);
+}
+
+/* Support 10, 100, 200, 1000, 2500, 10000 Mbps (e.g. 88E6190X) */
+int mv88e6390x_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed)
+{
+ if (!speed)
+ speed = port < 9 ? 1000 : 10000;
+
+ if (speed == 200 && port != 0)
+ return -EOPNOTSUPP;
+
+ if (speed >= 2500 && port < 9)
+ return -EOPNOTSUPP;
+
+ return mv88e6xxx_port_set_speed(chip, port, speed, true, true);
+}
+
/* Offset 0x04: Port Control Register */

static const char * const mv88e6xxx_port_state_names[] = {
diff --git a/drivers/net/dsa/mv88e6xxx/port.h b/drivers/net/dsa/mv88e6xxx/port.h
index 3472b79..499129c 100644
--- a/drivers/net/dsa/mv88e6xxx/port.h
+++ b/drivers/net/dsa/mv88e6xxx/port.h
@@ -30,6 +30,12 @@ int mv88e6xxx_port_set_link(struct mv88e6xxx_chip *chip, int port, int link);

int mv88e6xxx_port_set_duplex(struct mv88e6xxx_chip *chip, int port, int dup);

+int mv88e6065_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed);
+int mv88e6185_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed);
+int mv88e6352_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed);
+int mv88e6390_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed);
+int mv88e6390x_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed);
+
int mv88e6xxx_port_set_state(struct mv88e6xxx_chip *chip, int port, u8 state);

int mv88e6xxx_port_set_vlan_map(struct mv88e6xxx_chip *chip, int port, u16 map);
--
2.10.2

2016-11-01 03:23:47

by Vivien Didelot

[permalink] [raw]
Subject: [PATCH net-next 08/11] net: dsa: mv88e6xxx: add port duplex setter

Similarly to port's link, add setter to force port's half duplex, full
duplex or let normal duplex detection occurs.

Signed-off-by: Vivien Didelot <[email protected]>
---
drivers/net/dsa/mv88e6xxx/chip.c | 17 +++++++++++++++++
drivers/net/dsa/mv88e6xxx/mv88e6xxx.h | 7 +++++++
drivers/net/dsa/mv88e6xxx/port.c | 34 ++++++++++++++++++++++++++++++++++
drivers/net/dsa/mv88e6xxx/port.h | 2 ++
4 files changed, 60 insertions(+)

diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c
index cc43e6f..49a6935 100644
--- a/drivers/net/dsa/mv88e6xxx/chip.c
+++ b/drivers/net/dsa/mv88e6xxx/chip.c
@@ -3161,6 +3161,7 @@ static const struct mv88e6xxx_ops mv88e6085_ops = {
.phy_read = mv88e6xxx_phy_ppu_read,
.phy_write = mv88e6xxx_phy_ppu_write,
.port_set_link = mv88e6xxx_port_set_link,
+ .port_set_duplex = mv88e6xxx_port_set_duplex,
};

static const struct mv88e6xxx_ops mv88e6095_ops = {
@@ -3168,6 +3169,7 @@ static const struct mv88e6xxx_ops mv88e6095_ops = {
.phy_read = mv88e6xxx_phy_ppu_read,
.phy_write = mv88e6xxx_phy_ppu_write,
.port_set_link = mv88e6xxx_port_set_link,
+ .port_set_duplex = mv88e6xxx_port_set_duplex,
};

static const struct mv88e6xxx_ops mv88e6123_ops = {
@@ -3175,6 +3177,7 @@ static const struct mv88e6xxx_ops mv88e6123_ops = {
.phy_read = mv88e6xxx_read,
.phy_write = mv88e6xxx_write,
.port_set_link = mv88e6xxx_port_set_link,
+ .port_set_duplex = mv88e6xxx_port_set_duplex,
};

static const struct mv88e6xxx_ops mv88e6131_ops = {
@@ -3182,6 +3185,7 @@ static const struct mv88e6xxx_ops mv88e6131_ops = {
.phy_read = mv88e6xxx_phy_ppu_read,
.phy_write = mv88e6xxx_phy_ppu_write,
.port_set_link = mv88e6xxx_port_set_link,
+ .port_set_duplex = mv88e6xxx_port_set_duplex,
};

static const struct mv88e6xxx_ops mv88e6161_ops = {
@@ -3189,6 +3193,7 @@ static const struct mv88e6xxx_ops mv88e6161_ops = {
.phy_read = mv88e6xxx_read,
.phy_write = mv88e6xxx_write,
.port_set_link = mv88e6xxx_port_set_link,
+ .port_set_duplex = mv88e6xxx_port_set_duplex,
};

static const struct mv88e6xxx_ops mv88e6165_ops = {
@@ -3196,6 +3201,7 @@ static const struct mv88e6xxx_ops mv88e6165_ops = {
.phy_read = mv88e6xxx_read,
.phy_write = mv88e6xxx_write,
.port_set_link = mv88e6xxx_port_set_link,
+ .port_set_duplex = mv88e6xxx_port_set_duplex,
};

static const struct mv88e6xxx_ops mv88e6171_ops = {
@@ -3203,6 +3209,7 @@ static const struct mv88e6xxx_ops mv88e6171_ops = {
.phy_read = mv88e6xxx_g2_smi_phy_read,
.phy_write = mv88e6xxx_g2_smi_phy_write,
.port_set_link = mv88e6xxx_port_set_link,
+ .port_set_duplex = mv88e6xxx_port_set_duplex,
};

static const struct mv88e6xxx_ops mv88e6172_ops = {
@@ -3212,6 +3219,7 @@ static const struct mv88e6xxx_ops mv88e6172_ops = {
.phy_read = mv88e6xxx_g2_smi_phy_read,
.phy_write = mv88e6xxx_g2_smi_phy_write,
.port_set_link = mv88e6xxx_port_set_link,
+ .port_set_duplex = mv88e6xxx_port_set_duplex,
};

static const struct mv88e6xxx_ops mv88e6175_ops = {
@@ -3219,6 +3227,7 @@ static const struct mv88e6xxx_ops mv88e6175_ops = {
.phy_read = mv88e6xxx_g2_smi_phy_read,
.phy_write = mv88e6xxx_g2_smi_phy_write,
.port_set_link = mv88e6xxx_port_set_link,
+ .port_set_duplex = mv88e6xxx_port_set_duplex,
};

static const struct mv88e6xxx_ops mv88e6176_ops = {
@@ -3228,6 +3237,7 @@ static const struct mv88e6xxx_ops mv88e6176_ops = {
.phy_read = mv88e6xxx_g2_smi_phy_read,
.phy_write = mv88e6xxx_g2_smi_phy_write,
.port_set_link = mv88e6xxx_port_set_link,
+ .port_set_duplex = mv88e6xxx_port_set_duplex,
};

static const struct mv88e6xxx_ops mv88e6185_ops = {
@@ -3235,6 +3245,7 @@ static const struct mv88e6xxx_ops mv88e6185_ops = {
.phy_read = mv88e6xxx_phy_ppu_read,
.phy_write = mv88e6xxx_phy_ppu_write,
.port_set_link = mv88e6xxx_port_set_link,
+ .port_set_duplex = mv88e6xxx_port_set_duplex,
};

static const struct mv88e6xxx_ops mv88e6240_ops = {
@@ -3244,6 +3255,7 @@ static const struct mv88e6xxx_ops mv88e6240_ops = {
.phy_read = mv88e6xxx_g2_smi_phy_read,
.phy_write = mv88e6xxx_g2_smi_phy_write,
.port_set_link = mv88e6xxx_port_set_link,
+ .port_set_duplex = mv88e6xxx_port_set_duplex,
};

static const struct mv88e6xxx_ops mv88e6320_ops = {
@@ -3253,6 +3265,7 @@ static const struct mv88e6xxx_ops mv88e6320_ops = {
.phy_read = mv88e6xxx_g2_smi_phy_read,
.phy_write = mv88e6xxx_g2_smi_phy_write,
.port_set_link = mv88e6xxx_port_set_link,
+ .port_set_duplex = mv88e6xxx_port_set_duplex,
};

static const struct mv88e6xxx_ops mv88e6321_ops = {
@@ -3262,6 +3275,7 @@ static const struct mv88e6xxx_ops mv88e6321_ops = {
.phy_read = mv88e6xxx_g2_smi_phy_read,
.phy_write = mv88e6xxx_g2_smi_phy_write,
.port_set_link = mv88e6xxx_port_set_link,
+ .port_set_duplex = mv88e6xxx_port_set_duplex,
};

static const struct mv88e6xxx_ops mv88e6350_ops = {
@@ -3269,6 +3283,7 @@ static const struct mv88e6xxx_ops mv88e6350_ops = {
.phy_read = mv88e6xxx_g2_smi_phy_read,
.phy_write = mv88e6xxx_g2_smi_phy_write,
.port_set_link = mv88e6xxx_port_set_link,
+ .port_set_duplex = mv88e6xxx_port_set_duplex,
};

static const struct mv88e6xxx_ops mv88e6351_ops = {
@@ -3276,6 +3291,7 @@ static const struct mv88e6xxx_ops mv88e6351_ops = {
.phy_read = mv88e6xxx_g2_smi_phy_read,
.phy_write = mv88e6xxx_g2_smi_phy_write,
.port_set_link = mv88e6xxx_port_set_link,
+ .port_set_duplex = mv88e6xxx_port_set_duplex,
};

static const struct mv88e6xxx_ops mv88e6352_ops = {
@@ -3285,6 +3301,7 @@ static const struct mv88e6xxx_ops mv88e6352_ops = {
.phy_read = mv88e6xxx_g2_smi_phy_read,
.phy_write = mv88e6xxx_g2_smi_phy_write,
.port_set_link = mv88e6xxx_port_set_link,
+ .port_set_duplex = mv88e6xxx_port_set_duplex,
};

static const struct mv88e6xxx_info mv88e6xxx_table[] = {
diff --git a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h
index f55b829..35420ed 100644
--- a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h
+++ b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h
@@ -735,6 +735,13 @@ struct mv88e6xxx_ops {
* otherwise force link up.
*/
int (*port_set_link)(struct mv88e6xxx_chip *chip, int port, int link);
+
+ /* Port's MAC duplex mode
+ *
+ * Use DUPLEX_HALF or DUPLEX_FULL to force half or full duplex, or
+ * DUPLEX_UNKNOWN for normal duplex detection.
+ */
+ int (*port_set_duplex)(struct mv88e6xxx_chip *chip, int port, int dup);
};

enum stat_type {
diff --git a/drivers/net/dsa/mv88e6xxx/port.c b/drivers/net/dsa/mv88e6xxx/port.c
index 3b6c7c0..386db46 100644
--- a/drivers/net/dsa/mv88e6xxx/port.c
+++ b/drivers/net/dsa/mv88e6xxx/port.c
@@ -63,6 +63,40 @@ int mv88e6xxx_port_set_link(struct mv88e6xxx_chip *chip, int port, int link)
return 0;
}

+int mv88e6xxx_port_set_duplex(struct mv88e6xxx_chip *chip, int port, int dup)
+{
+ u16 reg;
+ int err;
+
+ err = mv88e6xxx_port_read(chip, port, PORT_PCS_CTRL, &reg);
+ if (err)
+ return err;
+
+ reg &= ~(PORT_PCS_CTRL_FORCE_DUPLEX | PORT_PCS_CTRL_DUPLEX_FULL);
+
+ switch (dup) {
+ case DUPLEX_HALF:
+ reg |= PORT_PCS_CTRL_FORCE_DUPLEX;
+ break;
+ case DUPLEX_FULL:
+ reg |= PORT_PCS_CTRL_FORCE_DUPLEX | PORT_PCS_CTRL_DUPLEX_FULL;
+ break;
+ case DUPLEX_UNKNOWN:
+ /* normal duplex detection */
+ break;
+ }
+
+ err = mv88e6xxx_port_write(chip, port, PORT_PCS_CTRL, reg);
+ if (err)
+ return err;
+
+ netdev_dbg(chip->ds->ports[port].netdev, "%s %s duplex\n",
+ reg & PORT_PCS_CTRL_FORCE_DUPLEX ? "Force" : "Unforce",
+ reg & PORT_PCS_CTRL_DUPLEX_FULL ? "full" : "half");
+
+ return 0;
+}
+
/* Offset 0x04: Port Control Register */

static const char * const mv88e6xxx_port_state_names[] = {
diff --git a/drivers/net/dsa/mv88e6xxx/port.h b/drivers/net/dsa/mv88e6xxx/port.h
index 87e7249..f73e90e 100644
--- a/drivers/net/dsa/mv88e6xxx/port.h
+++ b/drivers/net/dsa/mv88e6xxx/port.h
@@ -23,6 +23,8 @@ int mv88e6xxx_port_write(struct mv88e6xxx_chip *chip, int port, int reg,

int mv88e6xxx_port_set_link(struct mv88e6xxx_chip *chip, int port, int link);

+int mv88e6xxx_port_set_duplex(struct mv88e6xxx_chip *chip, int port, int dup);
+
int mv88e6xxx_port_set_state(struct mv88e6xxx_chip *chip, int port, u8 state);

int mv88e6xxx_port_set_vlan_map(struct mv88e6xxx_chip *chip, int port, u16 map);
--
2.10.2

2016-11-01 03:23:43

by Vivien Didelot

[permalink] [raw]
Subject: [PATCH net-next 06/11] net: dsa: mv88e6xxx: add port 802.1Q mode setter

Add port functions to set the port 802.1Q mode.

Signed-off-by: Vivien Didelot <[email protected]>
---
drivers/net/dsa/mv88e6xxx/chip.c | 33 ++-------------------------------
drivers/net/dsa/mv88e6xxx/port.c | 32 ++++++++++++++++++++++++++++++++
drivers/net/dsa/mv88e6xxx/port.h | 3 +++
3 files changed, 37 insertions(+), 31 deletions(-)

diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c
index 9c0a028..181d3b9 100644
--- a/drivers/net/dsa/mv88e6xxx/chip.c
+++ b/drivers/net/dsa/mv88e6xxx/chip.c
@@ -1806,48 +1806,19 @@ static int mv88e6xxx_port_check_hw_vlan(struct dsa_switch *ds, int port,
return err;
}

-static const char * const mv88e6xxx_port_8021q_mode_names[] = {
- [PORT_CONTROL_2_8021Q_DISABLED] = "Disabled",
- [PORT_CONTROL_2_8021Q_FALLBACK] = "Fallback",
- [PORT_CONTROL_2_8021Q_CHECK] = "Check",
- [PORT_CONTROL_2_8021Q_SECURE] = "Secure",
-};
-
static int mv88e6xxx_port_vlan_filtering(struct dsa_switch *ds, int port,
bool vlan_filtering)
{
struct mv88e6xxx_chip *chip = ds->priv;
- u16 old, new = vlan_filtering ? PORT_CONTROL_2_8021Q_SECURE :
+ u16 mode = vlan_filtering ? PORT_CONTROL_2_8021Q_SECURE :
PORT_CONTROL_2_8021Q_DISABLED;
- u16 reg;
int err;

if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_VTU))
return -EOPNOTSUPP;

mutex_lock(&chip->reg_lock);
-
- err = mv88e6xxx_port_read(chip, port, PORT_CONTROL_2, &reg);
- if (err)
- goto unlock;
-
- old = reg & PORT_CONTROL_2_8021Q_MASK;
-
- if (new != old) {
- reg &= ~PORT_CONTROL_2_8021Q_MASK;
- reg |= new & PORT_CONTROL_2_8021Q_MASK;
-
- err = mv88e6xxx_port_write(chip, port, PORT_CONTROL_2, reg);
- if (err)
- goto unlock;
-
- netdev_dbg(ds->ports[port].netdev, "802.1Q Mode %s (was %s)\n",
- mv88e6xxx_port_8021q_mode_names[new],
- mv88e6xxx_port_8021q_mode_names[old]);
- }
-
- err = 0;
-unlock:
+ err = mv88e6xxx_port_set_8021q_mode(chip, port, mode);
mutex_unlock(&chip->reg_lock);

return err;
diff --git a/drivers/net/dsa/mv88e6xxx/port.c b/drivers/net/dsa/mv88e6xxx/port.c
index 104fe2d..53d17e6 100644
--- a/drivers/net/dsa/mv88e6xxx/port.c
+++ b/drivers/net/dsa/mv88e6xxx/port.c
@@ -190,3 +190,35 @@ int mv88e6xxx_port_set_pvid(struct mv88e6xxx_chip *chip, int port, u16 pvid)

return 0;
}
+
+/* Offset 0x08: Port Control 2 Register */
+
+static const char * const mv88e6xxx_port_8021q_mode_names[] = {
+ [PORT_CONTROL_2_8021Q_DISABLED] = "Disabled",
+ [PORT_CONTROL_2_8021Q_FALLBACK] = "Fallback",
+ [PORT_CONTROL_2_8021Q_CHECK] = "Check",
+ [PORT_CONTROL_2_8021Q_SECURE] = "Secure",
+};
+
+int mv88e6xxx_port_set_8021q_mode(struct mv88e6xxx_chip *chip, int port,
+ u16 mode)
+{
+ u16 reg;
+ int err;
+
+ err = mv88e6xxx_port_read(chip, port, PORT_CONTROL_2, &reg);
+ if (err)
+ return err;
+
+ reg &= ~PORT_CONTROL_2_8021Q_MASK;
+ reg |= mode & PORT_CONTROL_2_8021Q_MASK;
+
+ err = mv88e6xxx_port_write(chip, port, PORT_CONTROL_2, reg);
+ if (err)
+ return err;
+
+ netdev_dbg(chip->ds->ports[port].netdev, "802.1QMode set to %s\n",
+ mv88e6xxx_port_8021q_mode_names[mode]);
+
+ return 0;
+}
diff --git a/drivers/net/dsa/mv88e6xxx/port.h b/drivers/net/dsa/mv88e6xxx/port.h
index 4489d9e..921eecf 100644
--- a/drivers/net/dsa/mv88e6xxx/port.h
+++ b/drivers/net/dsa/mv88e6xxx/port.h
@@ -31,4 +31,7 @@ int mv88e6xxx_port_set_fid(struct mv88e6xxx_chip *chip, int port, u16 fid);
int mv88e6xxx_port_get_pvid(struct mv88e6xxx_chip *chip, int port, u16 *pvid);
int mv88e6xxx_port_set_pvid(struct mv88e6xxx_chip *chip, int port, u16 pvid);

+int mv88e6xxx_port_set_8021q_mode(struct mv88e6xxx_chip *chip, int port,
+ u16 mode);
+
#endif /* _MV88E6XXX_PORT_H */
--
2.10.2

2016-11-01 03:23:45

by Vivien Didelot

[permalink] [raw]
Subject: [PATCH net-next 07/11] net: dsa: mv88e6xxx: add port link setter

Most of the chips will have a port register control bits to force the
port's link up, down, or let normal link detection occurs.

Implement such operation to use it later when setting duplex, etc.

Signed-off-by: Vivien Didelot <[email protected]>
---
drivers/net/dsa/mv88e6xxx/chip.c | 17 +++++++++++++++++
drivers/net/dsa/mv88e6xxx/mv88e6xxx.h | 8 ++++++++
drivers/net/dsa/mv88e6xxx/port.c | 33 +++++++++++++++++++++++++++++++++
drivers/net/dsa/mv88e6xxx/port.h | 2 ++
4 files changed, 60 insertions(+)

diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c
index 181d3b9..cc43e6f 100644
--- a/drivers/net/dsa/mv88e6xxx/chip.c
+++ b/drivers/net/dsa/mv88e6xxx/chip.c
@@ -3160,42 +3160,49 @@ static const struct mv88e6xxx_ops mv88e6085_ops = {
.set_switch_mac = mv88e6xxx_g1_set_switch_mac,
.phy_read = mv88e6xxx_phy_ppu_read,
.phy_write = mv88e6xxx_phy_ppu_write,
+ .port_set_link = mv88e6xxx_port_set_link,
};

static const struct mv88e6xxx_ops mv88e6095_ops = {
.set_switch_mac = mv88e6xxx_g1_set_switch_mac,
.phy_read = mv88e6xxx_phy_ppu_read,
.phy_write = mv88e6xxx_phy_ppu_write,
+ .port_set_link = mv88e6xxx_port_set_link,
};

static const struct mv88e6xxx_ops mv88e6123_ops = {
.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
.phy_read = mv88e6xxx_read,
.phy_write = mv88e6xxx_write,
+ .port_set_link = mv88e6xxx_port_set_link,
};

static const struct mv88e6xxx_ops mv88e6131_ops = {
.set_switch_mac = mv88e6xxx_g1_set_switch_mac,
.phy_read = mv88e6xxx_phy_ppu_read,
.phy_write = mv88e6xxx_phy_ppu_write,
+ .port_set_link = mv88e6xxx_port_set_link,
};

static const struct mv88e6xxx_ops mv88e6161_ops = {
.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
.phy_read = mv88e6xxx_read,
.phy_write = mv88e6xxx_write,
+ .port_set_link = mv88e6xxx_port_set_link,
};

static const struct mv88e6xxx_ops mv88e6165_ops = {
.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
.phy_read = mv88e6xxx_read,
.phy_write = mv88e6xxx_write,
+ .port_set_link = mv88e6xxx_port_set_link,
};

static const struct mv88e6xxx_ops mv88e6171_ops = {
.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,
};

static const struct mv88e6xxx_ops mv88e6172_ops = {
@@ -3204,12 +3211,14 @@ static const struct mv88e6xxx_ops mv88e6172_ops = {
.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,
};

static const struct mv88e6xxx_ops mv88e6175_ops = {
.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,
};

static const struct mv88e6xxx_ops mv88e6176_ops = {
@@ -3218,12 +3227,14 @@ static const struct mv88e6xxx_ops mv88e6176_ops = {
.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,
};

static const struct mv88e6xxx_ops mv88e6185_ops = {
.set_switch_mac = mv88e6xxx_g1_set_switch_mac,
.phy_read = mv88e6xxx_phy_ppu_read,
.phy_write = mv88e6xxx_phy_ppu_write,
+ .port_set_link = mv88e6xxx_port_set_link,
};

static const struct mv88e6xxx_ops mv88e6240_ops = {
@@ -3232,6 +3243,7 @@ static const struct mv88e6xxx_ops mv88e6240_ops = {
.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,
};

static const struct mv88e6xxx_ops mv88e6320_ops = {
@@ -3240,6 +3252,7 @@ static const struct mv88e6xxx_ops mv88e6320_ops = {
.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,
};

static const struct mv88e6xxx_ops mv88e6321_ops = {
@@ -3248,18 +3261,21 @@ static const struct mv88e6xxx_ops mv88e6321_ops = {
.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,
};

static const struct mv88e6xxx_ops mv88e6350_ops = {
.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,
};

static const struct mv88e6xxx_ops mv88e6351_ops = {
.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,
};

static const struct mv88e6xxx_ops mv88e6352_ops = {
@@ -3268,6 +3284,7 @@ static const struct mv88e6xxx_ops mv88e6352_ops = {
.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,
};

static const struct mv88e6xxx_info mv88e6xxx_table[] = {
diff --git a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h
index 6f48e58..f55b829 100644
--- a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h
+++ b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h
@@ -727,6 +727,14 @@ struct mv88e6xxx_ops {
u16 *val);
int (*phy_write)(struct mv88e6xxx_chip *chip, int addr, int reg,
u16 val);
+
+#define LINK_UNKNOWN -1
+
+ /* Port's MAC link state
+ * LINK_UNKNOWN for normal link detection, 0 to force link down,
+ * otherwise force link up.
+ */
+ int (*port_set_link)(struct mv88e6xxx_chip *chip, int port, int link);
};

enum stat_type {
diff --git a/drivers/net/dsa/mv88e6xxx/port.c b/drivers/net/dsa/mv88e6xxx/port.c
index 53d17e6..3b6c7c0 100644
--- a/drivers/net/dsa/mv88e6xxx/port.c
+++ b/drivers/net/dsa/mv88e6xxx/port.c
@@ -30,6 +30,39 @@ int mv88e6xxx_port_write(struct mv88e6xxx_chip *chip, int port, int reg,
return mv88e6xxx_write(chip, addr, reg, val);
}

+/* Offset 0x01: MAC (or PCS or Physical) Control Register
+ *
+ * Link, Duplex and Flow Control have one force bit, one value bit.
+ */
+
+int mv88e6xxx_port_set_link(struct mv88e6xxx_chip *chip, int port, int link)
+{
+ u16 reg;
+ int err;
+
+ err = mv88e6xxx_port_read(chip, port, PORT_PCS_CTRL, &reg);
+ if (err)
+ return err;
+
+ reg &= ~(PORT_PCS_CTRL_FORCE_LINK | PORT_PCS_CTRL_LINK_UP);
+
+ if (link != LINK_UNKNOWN) {
+ reg |= PORT_PCS_CTRL_FORCE_LINK;
+ if (link)
+ reg |= PORT_PCS_CTRL_LINK_UP;
+ }
+
+ err = mv88e6xxx_port_write(chip, port, PORT_PCS_CTRL, reg);
+ if (err)
+ return err;
+
+ netdev_dbg(chip->ds->ports[port].netdev, "%s link %s\n",
+ reg & PORT_PCS_CTRL_FORCE_LINK ? "Force" : "Unforce",
+ reg & PORT_PCS_CTRL_LINK_UP ? "up" : "down");
+
+ return 0;
+}
+
/* Offset 0x04: Port Control Register */

static const char * const mv88e6xxx_port_state_names[] = {
diff --git a/drivers/net/dsa/mv88e6xxx/port.h b/drivers/net/dsa/mv88e6xxx/port.h
index 921eecf..87e7249 100644
--- a/drivers/net/dsa/mv88e6xxx/port.h
+++ b/drivers/net/dsa/mv88e6xxx/port.h
@@ -21,6 +21,8 @@ int mv88e6xxx_port_read(struct mv88e6xxx_chip *chip, int port, int reg,
int mv88e6xxx_port_write(struct mv88e6xxx_chip *chip, int port, int reg,
u16 val);

+int mv88e6xxx_port_set_link(struct mv88e6xxx_chip *chip, int port, int link);
+
int mv88e6xxx_port_set_state(struct mv88e6xxx_chip *chip, int port, u8 state);

int mv88e6xxx_port_set_vlan_map(struct mv88e6xxx_chip *chip, int port, u16 map);
--
2.10.2

2016-11-01 03:23:41

by Vivien Didelot

[permalink] [raw]
Subject: [PATCH net-next 05/11] net: dsa: mv88e6xxx: add port PVID accessors

Add port functions to access the ports default VID.

Signed-off-by: Vivien Didelot <[email protected]>
---
drivers/net/dsa/mv88e6xxx/chip.c | 51 ++++------------------------------------
drivers/net/dsa/mv88e6xxx/port.c | 38 ++++++++++++++++++++++++++++++
drivers/net/dsa/mv88e6xxx/port.h | 3 +++
3 files changed, 45 insertions(+), 47 deletions(-)

diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c
index 20f59f1..9c0a028 100644
--- a/drivers/net/dsa/mv88e6xxx/chip.c
+++ b/drivers/net/dsa/mv88e6xxx/chip.c
@@ -1288,49 +1288,6 @@ static void mv88e6xxx_port_fast_age(struct dsa_switch *ds, int port)
netdev_err(ds->ports[port].netdev, "failed to flush ATU\n");
}

-static int _mv88e6xxx_port_pvid(struct mv88e6xxx_chip *chip, int port,
- u16 *new, u16 *old)
-{
- struct dsa_switch *ds = chip->ds;
- u16 pvid, reg;
- int err;
-
- err = mv88e6xxx_port_read(chip, port, PORT_DEFAULT_VLAN, &reg);
- if (err)
- return err;
-
- pvid = reg & PORT_DEFAULT_VLAN_MASK;
-
- if (new) {
- reg &= ~PORT_DEFAULT_VLAN_MASK;
- reg |= *new & PORT_DEFAULT_VLAN_MASK;
-
- err = mv88e6xxx_port_write(chip, port, PORT_DEFAULT_VLAN, reg);
- if (err)
- return err;
-
- netdev_dbg(ds->ports[port].netdev,
- "DefaultVID %d (was %d)\n", *new, pvid);
- }
-
- if (old)
- *old = pvid;
-
- return 0;
-}
-
-static int _mv88e6xxx_port_pvid_get(struct mv88e6xxx_chip *chip,
- int port, u16 *pvid)
-{
- return _mv88e6xxx_port_pvid(chip, port, NULL, pvid);
-}
-
-static int _mv88e6xxx_port_pvid_set(struct mv88e6xxx_chip *chip,
- int port, u16 pvid)
-{
- return _mv88e6xxx_port_pvid(chip, port, &pvid, NULL);
-}
-
static int _mv88e6xxx_vtu_wait(struct mv88e6xxx_chip *chip)
{
return mv88e6xxx_g1_wait(chip, GLOBAL_VTU_OP, GLOBAL_VTU_OP_BUSY);
@@ -1510,7 +1467,7 @@ static int mv88e6xxx_port_vlan_dump(struct dsa_switch *ds, int port,

mutex_lock(&chip->reg_lock);

- err = _mv88e6xxx_port_pvid_get(chip, port, &pvid);
+ err = mv88e6xxx_port_get_pvid(chip, port, &pvid);
if (err)
goto unlock;

@@ -1958,7 +1915,7 @@ static void mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port,
"failed to add VLAN %d%c\n",
vid, untagged ? 'u' : 't');

- if (pvid && _mv88e6xxx_port_pvid_set(chip, port, vlan->vid_end))
+ if (pvid && mv88e6xxx_port_set_pvid(chip, port, vlan->vid_end))
netdev_err(ds->ports[port].netdev, "failed to set PVID %d\n",
vlan->vid_end);

@@ -2013,7 +1970,7 @@ static int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port,

mutex_lock(&chip->reg_lock);

- err = _mv88e6xxx_port_pvid_get(chip, port, &pvid);
+ err = mv88e6xxx_port_get_pvid(chip, port, &pvid);
if (err)
goto unlock;

@@ -2023,7 +1980,7 @@ static int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port,
goto unlock;

if (vid == pvid) {
- err = _mv88e6xxx_port_pvid_set(chip, port, 0);
+ err = mv88e6xxx_port_set_pvid(chip, port, 0);
if (err)
goto unlock;
}
diff --git a/drivers/net/dsa/mv88e6xxx/port.c b/drivers/net/dsa/mv88e6xxx/port.c
index a7da812..104fe2d 100644
--- a/drivers/net/dsa/mv88e6xxx/port.c
+++ b/drivers/net/dsa/mv88e6xxx/port.c
@@ -152,3 +152,41 @@ int mv88e6xxx_port_set_fid(struct mv88e6xxx_chip *chip, int port, u16 fid)

return 0;
}
+
+/* Offset 0x07: Default Port VLAN ID & Priority */
+
+int mv88e6xxx_port_get_pvid(struct mv88e6xxx_chip *chip, int port, u16 *pvid)
+{
+ u16 reg;
+ int err;
+
+ err = mv88e6xxx_port_read(chip, port, PORT_DEFAULT_VLAN, &reg);
+ if (err)
+ return err;
+
+ *pvid = reg & PORT_DEFAULT_VLAN_MASK;
+
+ return 0;
+}
+
+int mv88e6xxx_port_set_pvid(struct mv88e6xxx_chip *chip, int port, u16 pvid)
+{
+ u16 reg;
+ int err;
+
+ err = mv88e6xxx_port_read(chip, port, PORT_DEFAULT_VLAN, &reg);
+ if (err)
+ return err;
+
+ reg &= ~PORT_DEFAULT_VLAN_MASK;
+ reg |= pvid & PORT_DEFAULT_VLAN_MASK;
+
+ err = mv88e6xxx_port_write(chip, port, PORT_DEFAULT_VLAN, reg);
+ if (err)
+ return err;
+
+ netdev_dbg(chip->ds->ports[port].netdev, "DefaultVID set to %u\n",
+ pvid);
+
+ return 0;
+}
diff --git a/drivers/net/dsa/mv88e6xxx/port.h b/drivers/net/dsa/mv88e6xxx/port.h
index 0df29b9..4489d9e 100644
--- a/drivers/net/dsa/mv88e6xxx/port.h
+++ b/drivers/net/dsa/mv88e6xxx/port.h
@@ -28,4 +28,7 @@ int mv88e6xxx_port_set_vlan_map(struct mv88e6xxx_chip *chip, int port, u16 map);
int mv88e6xxx_port_get_fid(struct mv88e6xxx_chip *chip, int port, u16 *fid);
int mv88e6xxx_port_set_fid(struct mv88e6xxx_chip *chip, int port, u16 fid);

+int mv88e6xxx_port_get_pvid(struct mv88e6xxx_chip *chip, int port, u16 *pvid);
+int mv88e6xxx_port_set_pvid(struct mv88e6xxx_chip *chip, int port, u16 pvid);
+
#endif /* _MV88E6XXX_PORT_H */
--
2.10.2

2016-11-01 03:23:38

by Vivien Didelot

[permalink] [raw]
Subject: [PATCH net-next 04/11] net: dsa: mv88e6xxx: add port FID accessors

Add functions to port files to access the ports default FID.

Signed-off-by: Vivien Didelot <[email protected]>
---
drivers/net/dsa/mv88e6xxx/chip.c | 77 +++-------------------------------------
drivers/net/dsa/mv88e6xxx/port.c | 67 ++++++++++++++++++++++++++++++++++
drivers/net/dsa/mv88e6xxx/port.h | 3 ++
3 files changed, 74 insertions(+), 73 deletions(-)

diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c
index 0877777..20f59f1 100644
--- a/drivers/net/dsa/mv88e6xxx/chip.c
+++ b/drivers/net/dsa/mv88e6xxx/chip.c
@@ -1674,75 +1674,6 @@ static int _mv88e6xxx_stu_loadpurge(struct mv88e6xxx_chip *chip,
return _mv88e6xxx_vtu_cmd(chip, GLOBAL_VTU_OP_STU_LOAD_PURGE);
}

-static int _mv88e6xxx_port_fid(struct mv88e6xxx_chip *chip, int port,
- u16 *new, u16 *old)
-{
- struct dsa_switch *ds = chip->ds;
- u16 upper_mask;
- u16 fid;
- u16 reg;
- int err;
-
- if (mv88e6xxx_num_databases(chip) == 4096)
- upper_mask = 0xff;
- else if (mv88e6xxx_num_databases(chip) == 256)
- upper_mask = 0xf;
- else
- return -EOPNOTSUPP;
-
- /* Port's default FID bits 3:0 are located in reg 0x06, offset 12 */
- err = mv88e6xxx_port_read(chip, port, PORT_BASE_VLAN, &reg);
- if (err)
- return err;
-
- fid = (reg & PORT_BASE_VLAN_FID_3_0_MASK) >> 12;
-
- if (new) {
- reg &= ~PORT_BASE_VLAN_FID_3_0_MASK;
- reg |= (*new << 12) & PORT_BASE_VLAN_FID_3_0_MASK;
-
- err = mv88e6xxx_port_write(chip, port, PORT_BASE_VLAN, reg);
- if (err)
- return err;
- }
-
- /* Port's default FID bits 11:4 are located in reg 0x05, offset 0 */
- err = mv88e6xxx_port_read(chip, port, PORT_CONTROL_1, &reg);
- if (err)
- return err;
-
- fid |= (reg & upper_mask) << 4;
-
- if (new) {
- reg &= ~upper_mask;
- reg |= (*new >> 4) & upper_mask;
-
- err = mv88e6xxx_port_write(chip, port, PORT_CONTROL_1, reg);
- if (err)
- return err;
-
- netdev_dbg(ds->ports[port].netdev,
- "FID %d (was %d)\n", *new, fid);
- }
-
- if (old)
- *old = fid;
-
- return 0;
-}
-
-static int _mv88e6xxx_port_fid_get(struct mv88e6xxx_chip *chip,
- int port, u16 *fid)
-{
- return _mv88e6xxx_port_fid(chip, port, NULL, fid);
-}
-
-static int _mv88e6xxx_port_fid_set(struct mv88e6xxx_chip *chip,
- int port, u16 fid)
-{
- return _mv88e6xxx_port_fid(chip, port, &fid, NULL);
-}
-
static int _mv88e6xxx_fid_new(struct mv88e6xxx_chip *chip, u16 *fid)
{
DECLARE_BITMAP(fid_bitmap, MV88E6XXX_N_FID);
@@ -1753,7 +1684,7 @@ static int _mv88e6xxx_fid_new(struct mv88e6xxx_chip *chip, u16 *fid)

/* Set every FID bit used by the (un)bridged ports */
for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) {
- err = _mv88e6xxx_port_fid_get(chip, i, fid);
+ err = mv88e6xxx_port_get_fid(chip, i, fid);
if (err)
return err;

@@ -2203,7 +2134,7 @@ static int mv88e6xxx_port_db_load_purge(struct mv88e6xxx_chip *chip, int port,

/* Null VLAN ID corresponds to the port private database */
if (vid == 0)
- err = _mv88e6xxx_port_fid_get(chip, port, &vlan.fid);
+ err = mv88e6xxx_port_get_fid(chip, port, &vlan.fid);
else
err = _mv88e6xxx_vtu_get(chip, vid, &vlan, false);
if (err)
@@ -2379,7 +2310,7 @@ static int mv88e6xxx_port_db_dump(struct mv88e6xxx_chip *chip, int port,
int err;

/* Dump port's default Filtering Information Database (VLAN ID 0) */
- err = _mv88e6xxx_port_fid_get(chip, port, &fid);
+ err = mv88e6xxx_port_get_fid(chip, port, &fid);
if (err)
return err;

@@ -2782,7 +2713,7 @@ static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port)
* database, and allow bidirectional communication between the
* CPU and DSA port(s), and the other ports.
*/
- err = _mv88e6xxx_port_fid_set(chip, port, 0);
+ err = mv88e6xxx_port_set_fid(chip, port, 0);
if (err)
return err;

diff --git a/drivers/net/dsa/mv88e6xxx/port.c b/drivers/net/dsa/mv88e6xxx/port.c
index c6a22ae..a7da812 100644
--- a/drivers/net/dsa/mv88e6xxx/port.c
+++ b/drivers/net/dsa/mv88e6xxx/port.c
@@ -61,6 +61,8 @@ int mv88e6xxx_port_set_state(struct mv88e6xxx_chip *chip, int port, u8 state)
return 0;
}

+/* Offset 0x05: Port Control 1 */
+
/* Offset 0x06: Port Based VLAN Map */

int mv88e6xxx_port_set_vlan_map(struct mv88e6xxx_chip *chip, int port, u16 map)
@@ -85,3 +87,68 @@ int mv88e6xxx_port_set_vlan_map(struct mv88e6xxx_chip *chip, int port, u16 map)

return 0;
}
+
+int mv88e6xxx_port_get_fid(struct mv88e6xxx_chip *chip, int port, u16 *fid)
+{
+ const u16 upper_mask = (mv88e6xxx_num_databases(chip) - 1) >> 4;
+ u16 reg;
+ int err;
+
+ /* Port's default FID lower 4 bits are located in reg 0x06, offset 12 */
+ err = mv88e6xxx_port_read(chip, port, PORT_BASE_VLAN, &reg);
+ if (err)
+ return err;
+
+ *fid = (reg & 0xf000) >> 12;
+
+ /* Port's default FID upper bits are located in reg 0x05, offset 0 */
+ if (upper_mask) {
+ err = mv88e6xxx_port_read(chip, port, PORT_CONTROL_1, &reg);
+ if (err)
+ return err;
+
+ *fid |= (reg & upper_mask) << 4;
+ }
+
+ return 0;
+}
+
+int mv88e6xxx_port_set_fid(struct mv88e6xxx_chip *chip, int port, u16 fid)
+{
+ const u16 upper_mask = (mv88e6xxx_num_databases(chip) - 1) >> 4;
+ u16 reg;
+ int err;
+
+ if (fid >= mv88e6xxx_num_databases(chip))
+ return -EINVAL;
+
+ /* Port's default FID lower 4 bits are located in reg 0x06, offset 12 */
+ err = mv88e6xxx_port_read(chip, port, PORT_BASE_VLAN, &reg);
+ if (err)
+ return err;
+
+ reg &= 0x0fff;
+ reg |= (fid & 0x000f) << 12;
+
+ err = mv88e6xxx_port_write(chip, port, PORT_BASE_VLAN, reg);
+ if (err)
+ return err;
+
+ /* Port's default FID upper bits are located in reg 0x05, offset 0 */
+ if (upper_mask) {
+ err = mv88e6xxx_port_read(chip, port, PORT_CONTROL_1, &reg);
+ if (err)
+ return err;
+
+ reg &= ~upper_mask;
+ reg |= (fid >> 4) & upper_mask;
+
+ err = mv88e6xxx_port_write(chip, port, PORT_CONTROL_1, reg);
+ if (err)
+ return err;
+ }
+
+ netdev_dbg(chip->ds->ports[port].netdev, "FID set to %u\n", fid);
+
+ return 0;
+}
diff --git a/drivers/net/dsa/mv88e6xxx/port.h b/drivers/net/dsa/mv88e6xxx/port.h
index 037d638..0df29b9 100644
--- a/drivers/net/dsa/mv88e6xxx/port.h
+++ b/drivers/net/dsa/mv88e6xxx/port.h
@@ -25,4 +25,7 @@ int mv88e6xxx_port_set_state(struct mv88e6xxx_chip *chip, int port, u8 state);

int mv88e6xxx_port_set_vlan_map(struct mv88e6xxx_chip *chip, int port, u16 map);

+int mv88e6xxx_port_get_fid(struct mv88e6xxx_chip *chip, int port, u16 *fid);
+int mv88e6xxx_port_set_fid(struct mv88e6xxx_chip *chip, int port, u16 fid);
+
#endif /* _MV88E6XXX_PORT_H */
--
2.10.2

2016-11-01 03:23:35

by Vivien Didelot

[permalink] [raw]
Subject: [PATCH net-next 03/11] net: dsa: mv88e6xxx: add port vlan map setter

Add a port function to access the Port Based VLAN Map register.

Signed-off-by: Vivien Didelot <[email protected]>
---
drivers/net/dsa/mv88e6xxx/chip.c | 14 ++------------
drivers/net/dsa/mv88e6xxx/port.c | 25 +++++++++++++++++++++++++
drivers/net/dsa/mv88e6xxx/port.h | 2 ++
3 files changed, 29 insertions(+), 12 deletions(-)

diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c
index 12c1175..0877777 100644
--- a/drivers/net/dsa/mv88e6xxx/chip.c
+++ b/drivers/net/dsa/mv88e6xxx/chip.c
@@ -1218,16 +1218,13 @@ static int _mv88e6xxx_atu_remove(struct mv88e6xxx_chip *chip, u16 fid,
static int _mv88e6xxx_port_based_vlan_map(struct mv88e6xxx_chip *chip, int port)
{
struct net_device *bridge = chip->ports[port].bridge_dev;
- const u16 mask = (1 << mv88e6xxx_num_ports(chip)) - 1;
struct dsa_switch *ds = chip->ds;
u16 output_ports = 0;
- u16 reg;
- int err;
int i;

/* allow CPU port or DSA link(s) to send frames to every port */
if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) {
- output_ports = mask;
+ output_ports = ~0;
} else {
for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) {
/* allow sending frames to every group member */
@@ -1243,14 +1240,7 @@ static int _mv88e6xxx_port_based_vlan_map(struct mv88e6xxx_chip *chip, int port)
/* prevent frames from going back out of the port they came in on */
output_ports &= ~BIT(port);

- err = mv88e6xxx_port_read(chip, port, PORT_BASE_VLAN, &reg);
- if (err)
- return err;
-
- reg &= ~mask;
- reg |= output_ports & mask;
-
- return mv88e6xxx_port_write(chip, port, PORT_BASE_VLAN, reg);
+ return mv88e6xxx_port_set_vlan_map(chip, port, output_ports);
}

static void mv88e6xxx_port_stp_state_set(struct dsa_switch *ds, int port,
diff --git a/drivers/net/dsa/mv88e6xxx/port.c b/drivers/net/dsa/mv88e6xxx/port.c
index 8d59fe7..c6a22ae 100644
--- a/drivers/net/dsa/mv88e6xxx/port.c
+++ b/drivers/net/dsa/mv88e6xxx/port.c
@@ -60,3 +60,28 @@ int mv88e6xxx_port_set_state(struct mv88e6xxx_chip *chip, int port, u8 state)

return 0;
}
+
+/* Offset 0x06: Port Based VLAN Map */
+
+int mv88e6xxx_port_set_vlan_map(struct mv88e6xxx_chip *chip, int port, u16 map)
+{
+ const u16 mask = GENMASK(mv88e6xxx_num_ports(chip) - 1, 0);
+ u16 reg;
+ int err;
+
+ err = mv88e6xxx_port_read(chip, port, PORT_BASE_VLAN, &reg);
+ if (err)
+ return err;
+
+ reg &= ~mask;
+ reg |= map & mask;
+
+ err = mv88e6xxx_port_write(chip, port, PORT_BASE_VLAN, reg);
+ if (err)
+ return err;
+
+ netdev_dbg(chip->ds->ports[port].netdev, "VLANTable set to %.3x\n",
+ map);
+
+ return 0;
+}
diff --git a/drivers/net/dsa/mv88e6xxx/port.h b/drivers/net/dsa/mv88e6xxx/port.h
index ac13988..037d638 100644
--- a/drivers/net/dsa/mv88e6xxx/port.h
+++ b/drivers/net/dsa/mv88e6xxx/port.h
@@ -23,4 +23,6 @@ int mv88e6xxx_port_write(struct mv88e6xxx_chip *chip, int port, int reg,

int mv88e6xxx_port_set_state(struct mv88e6xxx_chip *chip, int port, u8 state);

+int mv88e6xxx_port_set_vlan_map(struct mv88e6xxx_chip *chip, int port, u16 map);
+
#endif /* _MV88E6XXX_PORT_H */
--
2.10.2

2016-11-01 03:23:31

by Vivien Didelot

[permalink] [raw]
Subject: [PATCH net-next 11/11] net: dsa: mv88e6xxx: setup port's MAC

Now that we have setters to configure the port's MAC, use them to
refactor the port setup and adjust_link code.

Note that port's MAC speed, duplex or RGMII delay must not be changed
unless the port's link is forced down. So wrap all that in a
mv88e6xxx_port_ctrl_mac function.

Signed-off-by: Vivien Didelot <[email protected]>
---
drivers/net/dsa/mv88e6xxx/chip.c | 138 ++++++++++++++++-----------------------
1 file changed, 58 insertions(+), 80 deletions(-)

diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c
index 23c05e3..56d60fd 100644
--- a/drivers/net/dsa/mv88e6xxx/chip.c
+++ b/drivers/net/dsa/mv88e6xxx/chip.c
@@ -701,6 +701,47 @@ static bool mv88e6xxx_6352_family(struct mv88e6xxx_chip *chip)
return chip->info->family == MV88E6XXX_FAMILY_6352;
}

+static int mv88e6xxx_port_ctrl_mac(struct mv88e6xxx_chip *chip, int port,
+ int link, int speed, int duplex,
+ phy_interface_t mode)
+{
+ int err;
+
+ if (!chip->info->ops->port_set_link)
+ return 0;
+
+ /* Port's MAC control must not be changed unless the link is down */
+ err = chip->info->ops->port_set_link(chip, port, 0);
+ if (err)
+ return err;
+
+ if (chip->info->ops->port_set_speed) {
+ err = chip->info->ops->port_set_speed(chip, port, speed);
+ if (err && err != -EOPNOTSUPP)
+ goto restore_link;
+ }
+
+ if (chip->info->ops->port_set_duplex) {
+ err = chip->info->ops->port_set_duplex(chip, port, duplex);
+ if (err && err != -EOPNOTSUPP)
+ goto restore_link;
+ }
+
+ if (chip->info->ops->port_set_rgmii_delay) {
+ err = chip->info->ops->port_set_rgmii_delay(chip, port, mode);
+ if (err && err != -EOPNOTSUPP)
+ goto restore_link;
+ }
+
+ err = 0;
+restore_link:
+ if (chip->info->ops->port_set_link(chip, port, link))
+ netdev_err(chip->ds->ports[port].netdev,
+ "failed to restore MAC's link\n");
+
+ return err;
+}
+
/* We expect the switch to perform auto negotiation if there is a real
* phy. However, in the case of a fixed link phy, we force the port
* settings from the fixed link settings.
@@ -709,64 +750,18 @@ static void mv88e6xxx_adjust_link(struct dsa_switch *ds, int port,
struct phy_device *phydev)
{
struct mv88e6xxx_chip *chip = ds->priv;
- u16 reg;
int err;

if (!phy_is_pseudo_fixed_link(phydev))
return;

mutex_lock(&chip->reg_lock);
-
- err = mv88e6xxx_port_read(chip, port, PORT_PCS_CTRL, &reg);
- if (err)
- goto out;
-
- reg &= ~(PORT_PCS_CTRL_LINK_UP |
- PORT_PCS_CTRL_FORCE_LINK |
- PORT_PCS_CTRL_DUPLEX_FULL |
- PORT_PCS_CTRL_FORCE_DUPLEX |
- PORT_PCS_CTRL_SPEED_UNFORCED);
-
- reg |= PORT_PCS_CTRL_FORCE_LINK;
- if (phydev->link)
- reg |= PORT_PCS_CTRL_LINK_UP;
-
- if (mv88e6xxx_6065_family(chip) && phydev->speed > SPEED_100)
- goto out;
-
- switch (phydev->speed) {
- case SPEED_1000:
- reg |= PORT_PCS_CTRL_SPEED_1000;
- break;
- case SPEED_100:
- reg |= PORT_PCS_CTRL_SPEED_100;
- break;
- case SPEED_10:
- reg |= PORT_PCS_CTRL_SPEED_10;
- break;
- default:
- pr_info("Unknown speed");
- goto out;
- }
-
- reg |= PORT_PCS_CTRL_FORCE_DUPLEX;
- if (phydev->duplex == DUPLEX_FULL)
- reg |= PORT_PCS_CTRL_DUPLEX_FULL;
-
- if ((mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip)) &&
- (port >= mv88e6xxx_num_ports(chip) - 2)) {
- if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID)
- reg |= PORT_PCS_CTRL_RGMII_DELAY_RXCLK;
- if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID)
- reg |= PORT_PCS_CTRL_RGMII_DELAY_TXCLK;
- if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID)
- reg |= (PORT_PCS_CTRL_RGMII_DELAY_RXCLK |
- PORT_PCS_CTRL_RGMII_DELAY_TXCLK);
- }
- mv88e6xxx_port_write(chip, port, PORT_PCS_CTRL, reg);
-
-out:
+ err = mv88e6xxx_port_ctrl_mac(chip, port, phydev->link, phydev->speed,
+ phydev->duplex, phydev->interface);
mutex_unlock(&chip->reg_lock);
+
+ if (err && err != -EOPNOTSUPP)
+ netdev_err(ds->ports[port].netdev, "failed to configure MAC\n");
}

static int _mv88e6xxx_stats_wait(struct mv88e6xxx_chip *chip)
@@ -2409,35 +2404,18 @@ static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port)
int err;
u16 reg;

- if (mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip) ||
- mv88e6xxx_6165_family(chip) || mv88e6xxx_6097_family(chip) ||
- mv88e6xxx_6185_family(chip) || mv88e6xxx_6095_family(chip) ||
- mv88e6xxx_6065_family(chip) || mv88e6xxx_6320_family(chip)) {
- /* MAC Forcing register: don't force link, speed,
- * duplex or flow control state to any particular
- * values on physical ports, but force the CPU port
- * and all DSA ports to their maximum bandwidth and
- * full duplex.
- */
- err = mv88e6xxx_port_read(chip, port, PORT_PCS_CTRL, &reg);
- if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) {
- reg &= ~PORT_PCS_CTRL_SPEED_UNFORCED;
- reg |= PORT_PCS_CTRL_FORCE_LINK |
- PORT_PCS_CTRL_LINK_UP |
- PORT_PCS_CTRL_DUPLEX_FULL |
- PORT_PCS_CTRL_FORCE_DUPLEX;
- if (mv88e6xxx_6065_family(chip))
- reg |= PORT_PCS_CTRL_SPEED_100;
- else
- reg |= PORT_PCS_CTRL_SPEED_1000;
- } else {
- reg |= PORT_PCS_CTRL_SPEED_UNFORCED;
- }
-
- err = mv88e6xxx_port_write(chip, port, PORT_PCS_CTRL, reg);
- if (err)
- return err;
- }
+ /* MAC Forcing register: don't force link, speed, duplex or flow control
+ * state to any particular values on physical ports, but force the CPU
+ * port and all DSA ports to their maximum bandwidth and full duplex.
+ */
+ if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port))
+ err = mv88e6xxx_port_ctrl_mac(chip, port, 1, SPEED_MAX,
+ DUPLEX_FULL, 0);
+ else
+ err = mv88e6xxx_port_ctrl_mac(chip, port, LINK_UNKNOWN,
+ SPEED_UNKNOWN, DUPLEX_UNKNOWN, 0);
+ if (err)
+ return err;

/* Port Control: disable Drop-on-Unlock, disable Drop-on-Lock,
* disable Header mode, enable IGMP/MLD snooping, disable VLAN
--
2.10.2

2016-11-01 03:21:56

by Vivien Didelot

[permalink] [raw]
Subject: [PATCH net-next 02/11] net: dsa: mv88e6xxx: add port state setter

Add the port STP state setter to the port files.

Signed-off-by: Vivien Didelot <[email protected]>
---
drivers/net/dsa/mv88e6xxx/chip.c | 49 ++++------------------------------------
drivers/net/dsa/mv88e6xxx/port.c | 31 +++++++++++++++++++++++++
drivers/net/dsa/mv88e6xxx/port.h | 2 ++
3 files changed, 37 insertions(+), 45 deletions(-)

diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c
index b32b242..12c1175 100644
--- a/drivers/net/dsa/mv88e6xxx/chip.c
+++ b/drivers/net/dsa/mv88e6xxx/chip.c
@@ -1215,41 +1215,6 @@ static int _mv88e6xxx_atu_remove(struct mv88e6xxx_chip *chip, u16 fid,
return _mv88e6xxx_atu_move(chip, fid, port, 0x0f, static_too);
}

-static const char * const mv88e6xxx_port_state_names[] = {
- [PORT_CONTROL_STATE_DISABLED] = "Disabled",
- [PORT_CONTROL_STATE_BLOCKING] = "Blocking/Listening",
- [PORT_CONTROL_STATE_LEARNING] = "Learning",
- [PORT_CONTROL_STATE_FORWARDING] = "Forwarding",
-};
-
-static int _mv88e6xxx_port_state(struct mv88e6xxx_chip *chip, int port,
- u8 state)
-{
- struct dsa_switch *ds = chip->ds;
- u16 reg;
- int err;
- u8 oldstate;
-
- err = mv88e6xxx_port_read(chip, port, PORT_CONTROL, &reg);
- if (err)
- return err;
-
- oldstate = reg & PORT_CONTROL_STATE_MASK;
-
- reg &= ~PORT_CONTROL_STATE_MASK;
- reg |= state;
-
- err = mv88e6xxx_port_write(chip, port, PORT_CONTROL, reg);
- if (err)
- return err;
-
- netdev_dbg(ds->ports[port].netdev, "PortState %s (was %s)\n",
- mv88e6xxx_port_state_names[state],
- mv88e6xxx_port_state_names[oldstate]);
-
- return 0;
-}
-
static int _mv88e6xxx_port_based_vlan_map(struct mv88e6xxx_chip *chip, int port)
{
struct net_device *bridge = chip->ports[port].bridge_dev;
@@ -1313,13 +1278,11 @@ static void mv88e6xxx_port_stp_state_set(struct dsa_switch *ds, int port,
}

mutex_lock(&chip->reg_lock);
- err = _mv88e6xxx_port_state(chip, port, stp_state);
+ err = mv88e6xxx_port_set_state(chip, port, stp_state);
mutex_unlock(&chip->reg_lock);

if (err)
- netdev_err(ds->ports[port].netdev,
- "failed to update state to %s\n",
- mv88e6xxx_port_state_names[stp_state]);
+ netdev_err(ds->ports[port].netdev, "failed to update state\n");
}

static void mv88e6xxx_port_fast_age(struct dsa_switch *ds, int port)
@@ -2526,12 +2489,8 @@ static int mv88e6xxx_switch_reset(struct mv88e6xxx_chip *chip)

/* Set all ports to the disabled state. */
for (i = 0; i < mv88e6xxx_num_ports(chip); i++) {
- err = mv88e6xxx_port_read(chip, i, PORT_CONTROL, &reg);
- if (err)
- return err;
-
- err = mv88e6xxx_port_write(chip, i, PORT_CONTROL,
- reg & 0xfffc);
+ err = mv88e6xxx_port_set_state(chip, i,
+ PORT_CONTROL_STATE_DISABLED);
if (err)
return err;
}
diff --git a/drivers/net/dsa/mv88e6xxx/port.c b/drivers/net/dsa/mv88e6xxx/port.c
index 3b36c92..8d59fe7 100644
--- a/drivers/net/dsa/mv88e6xxx/port.c
+++ b/drivers/net/dsa/mv88e6xxx/port.c
@@ -29,3 +29,34 @@ int mv88e6xxx_port_write(struct mv88e6xxx_chip *chip, int port, int reg,

return mv88e6xxx_write(chip, addr, reg, val);
}
+
+/* Offset 0x04: Port Control Register */
+
+static const char * const mv88e6xxx_port_state_names[] = {
+ [PORT_CONTROL_STATE_DISABLED] = "Disabled",
+ [PORT_CONTROL_STATE_BLOCKING] = "Blocking/Listening",
+ [PORT_CONTROL_STATE_LEARNING] = "Learning",
+ [PORT_CONTROL_STATE_FORWARDING] = "Forwarding",
+};
+
+int mv88e6xxx_port_set_state(struct mv88e6xxx_chip *chip, int port, u8 state)
+{
+ u16 reg;
+ int err;
+
+ err = mv88e6xxx_port_read(chip, port, PORT_CONTROL, &reg);
+ if (err)
+ return err;
+
+ reg &= ~PORT_CONTROL_STATE_MASK;
+ reg |= state;
+
+ err = mv88e6xxx_port_write(chip, port, PORT_CONTROL, reg);
+ if (err)
+ return err;
+
+ netdev_dbg(chip->ds->ports[port].netdev, "PortState set to %s\n",
+ mv88e6xxx_port_state_names[state]);
+
+ return 0;
+}
diff --git a/drivers/net/dsa/mv88e6xxx/port.h b/drivers/net/dsa/mv88e6xxx/port.h
index ae1ae2b..ac13988 100644
--- a/drivers/net/dsa/mv88e6xxx/port.h
+++ b/drivers/net/dsa/mv88e6xxx/port.h
@@ -21,4 +21,6 @@ int mv88e6xxx_port_read(struct mv88e6xxx_chip *chip, int port, int reg,
int mv88e6xxx_port_write(struct mv88e6xxx_chip *chip, int port, int reg,
u16 val);

+int mv88e6xxx_port_set_state(struct mv88e6xxx_chip *chip, int port, u8 state);
+
#endif /* _MV88E6XXX_PORT_H */
--
2.10.2

2016-11-01 09:38:26

by Andrew Lunn

[permalink] [raw]
Subject: Re: [PATCH net-next 04/11] net: dsa: mv88e6xxx: add port FID accessors

> --- a/drivers/net/dsa/mv88e6xxx/port.c
> +++ b/drivers/net/dsa/mv88e6xxx/port.c
> @@ -61,6 +61,8 @@ int mv88e6xxx_port_set_state(struct mv88e6xxx_chip *chip, int port, u8 state)
> return 0;
> }
>
> +/* Offset 0x05: Port Control 1 */
> +
> /* Offset 0x06: Port Based VLAN Map */
>
> int mv88e6xxx_port_set_vlan_map(struct mv88e6xxx_chip *chip, int port, u16 map)

Hi Vivien

This comment seems in an odd place.

Andrew

2016-11-01 09:43:17

by Andrew Lunn

[permalink] [raw]
Subject: Re: [PATCH net-next 07/11] net: dsa: mv88e6xxx: add port link setter

> +#define LINK_UNKNOWN -1
> +
> + /* Port's MAC link state
> + * LINK_UNKNOWN for normal link detection, 0 to force link down,
> + * otherwise force link up.
> + */
> + int (*port_set_link)(struct mv88e6xxx_chip *chip, int port, int link);

Hi Vivien

Maybe LINK_AUTO would be better than UNKNOWN? Or LINK_UNFORCED.

Andrew

2016-11-01 09:44:44

by Andrew Lunn

[permalink] [raw]
Subject: Re: [PATCH net-next 08/11] net: dsa: mv88e6xxx: add port duplex setter

> int (*port_set_link)(struct mv88e6xxx_chip *chip, int port, int link);
> +
> + /* Port's MAC duplex mode
> + *
> + * Use DUPLEX_HALF or DUPLEX_FULL to force half or full duplex, or
> + * DUPLEX_UNKNOWN for normal duplex detection.
> + */
> + int (*port_set_duplex)(struct mv88e6xxx_chip *chip, int port, int dup);

Hi Vivien

Again, i think DUPLEX_UNFORCED, or DUPLEX_AUTO would be better.

Andrew

2016-11-02 01:01:30

by Vivien Didelot

[permalink] [raw]
Subject: Re: [PATCH net-next 04/11] net: dsa: mv88e6xxx: add port FID accessors

Hi Andrew,

Andrew Lunn <[email protected]> writes:

>> --- a/drivers/net/dsa/mv88e6xxx/port.c
>> +++ b/drivers/net/dsa/mv88e6xxx/port.c
>> @@ -61,6 +61,8 @@ int mv88e6xxx_port_set_state(struct mv88e6xxx_chip *chip, int port, u8 state)
>> return 0;
>> }
>>
>> +/* Offset 0x05: Port Control 1 */
>> +
>> /* Offset 0x06: Port Based VLAN Map */
>>
>> int mv88e6xxx_port_set_vlan_map(struct mv88e6xxx_chip *chip, int port, u16 map)
>
> This comment seems in an odd place.

It isn't. Port's default FID uses register 0x05 and I document them
above the related functions.

Thanks,

Vivien

2016-11-02 01:07:17

by Vivien Didelot

[permalink] [raw]
Subject: Re: [PATCH net-next 07/11] net: dsa: mv88e6xxx: add port link setter

Hi Andrew,

Andrew Lunn <[email protected]> writes:

>> +#define LINK_UNKNOWN -1
>> +
>> + /* Port's MAC link state
>> + * LINK_UNKNOWN for normal link detection, 0 to force link down,
>> + * otherwise force link up.
>> + */
>> + int (*port_set_link)(struct mv88e6xxx_chip *chip, int port, int link);
>
> Maybe LINK_AUTO would be better than UNKNOWN? Or LINK_UNFORCED.

I used LINK_UNKNOWN to be consistent with the supported SPEED_UNKNOWN
and DUPLEX_UNKNOWN values of PHY devices.

Thanks,

Vivien

2016-11-02 01:12:12

by Vivien Didelot

[permalink] [raw]
Subject: Re: [PATCH net-next 08/11] net: dsa: mv88e6xxx: add port duplex setter

Hi Andrew,

Andrew Lunn <[email protected]> writes:

>> int (*port_set_link)(struct mv88e6xxx_chip *chip, int port, int link);
>> +
>> + /* Port's MAC duplex mode
>> + *
>> + * Use DUPLEX_HALF or DUPLEX_FULL to force half or full duplex, or
>> + * DUPLEX_UNKNOWN for normal duplex detection.
>> + */
>> + int (*port_set_duplex)(struct mv88e6xxx_chip *chip, int port, int dup);
>
> Again, i think DUPLEX_UNFORCED, or DUPLEX_AUTO would be better.

DUPLEX_UNKNOWN and SPEED_UNKNOWN are existing values used by the
phy/ethtool interface for the phy_device. I prefered to use them when
possible instead of creating new ones.

Thanks,

Vivien

2016-11-02 09:16:35

by Andrew Lunn

[permalink] [raw]
Subject: Re: [PATCH net-next 07/11] net: dsa: mv88e6xxx: add port link setter

On Wed, Nov 02, 2016 at 02:07:09AM +0100, Vivien Didelot wrote:
> Hi Andrew,
>
> Andrew Lunn <[email protected]> writes:
>
> >> +#define LINK_UNKNOWN -1
> >> +
> >> + /* Port's MAC link state
> >> + * LINK_UNKNOWN for normal link detection, 0 to force link down,
> >> + * otherwise force link up.
> >> + */
> >> + int (*port_set_link)(struct mv88e6xxx_chip *chip, int port, int link);
> >
> > Maybe LINK_AUTO would be better than UNKNOWN? Or LINK_UNFORCED.
>
> I used LINK_UNKNOWN to be consistent with the supported SPEED_UNKNOWN
> and DUPLEX_UNKNOWN values of PHY devices.

Hi Vivien

These are i think for reporting back to user space what duplex or link
is currently being used. But here you are setting, not
reporting. Setting something to an unknown state is rather odd, and in
fact, it is not unknown, it is unforced.

Andrew

2016-11-02 22:32:02

by Vivien Didelot

[permalink] [raw]
Subject: Re: [PATCH net-next 07/11] net: dsa: mv88e6xxx: add port link setter

Hi Andrew,

Andrew Lunn <[email protected]> writes:

> On Wed, Nov 02, 2016 at 02:07:09AM +0100, Vivien Didelot wrote:
>> Hi Andrew,
>>
>> Andrew Lunn <[email protected]> writes:
>>
>> >> +#define LINK_UNKNOWN -1
>> >> +
>> >> + /* Port's MAC link state
>> >> + * LINK_UNKNOWN for normal link detection, 0 to force link down,
>> >> + * otherwise force link up.
>> >> + */
>> >> + int (*port_set_link)(struct mv88e6xxx_chip *chip, int port, int link);
>> >
>> > Maybe LINK_AUTO would be better than UNKNOWN? Or LINK_UNFORCED.
>>
>> I used LINK_UNKNOWN to be consistent with the supported SPEED_UNKNOWN
>> and DUPLEX_UNKNOWN values of PHY devices.
>
> These are i think for reporting back to user space what duplex or link
> is currently being used. But here you are setting, not
> reporting. Setting something to an unknown state is rather odd, and in
> fact, it is not unknown, it is unforced.

Do you expect to return an error if adjust_link is called with
phydev->duplex == DUPLEX_UNKNOWN, or, do you expect to fallback to
unforced duplex when setting such value?

Thanks,

Vivien

2016-11-02 23:05:22

by Andrew Lunn

[permalink] [raw]
Subject: Re: [PATCH net-next 07/11] net: dsa: mv88e6xxx: add port link setter

> Do you expect to return an error if adjust_link is called with
> phydev->duplex == DUPLEX_UNKNOWN, or, do you expect to fallback to
> unforced duplex when setting such value?

ethtool(1) itself does not allow you to specify "unknown". It only
allows "full" or "half". So passing DUPLEX_UNKNOWN means using the API
directly. The core ethtool code does not sanity check the request, so
will pass on DUPLEX_UNKNOWN to the drivers.

A quick search of the drivers, 99% seem to ignore DUPLEX_UNKNOWN. The
1% is bnx2x, which has:

/* If received a request for an unknown duplex, assume full*/
if (cmd->duplex == DUPLEX_UNKNOWN)
cmd->duplex = DUPLEX_FULL;

I personally would return -EINVAL, since it is unclear what
DUPLEX_UNKNOWN means. It could be argued that falling back to Half is
correct, since failed autoneg generally results in 10/Half. Every
Ethernet can do that, where as a device needs to be 25 years or
younger to support Full :-)

Andrew