netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC net-next 0/9] net: dsa: mv88e6xxx: add RMU support
@ 2019-05-21 19:29 Vivien Didelot
  2019-05-21 19:29 ` [RFC net-next 1/9] net: dsa: introduce dsa_master_find_switch Vivien Didelot
                   ` (8 more replies)
  0 siblings, 9 replies; 11+ messages in thread
From: Vivien Didelot @ 2019-05-21 19:29 UTC (permalink / raw)
  To: netdev
  Cc: cphealy, Vivien Didelot, Florian Fainelli, Andrew Lunn, David S. Miller

This series is a request for comment for the support for sending
and receiving special management frames between Linux and Ethernet
switch drivers.

The (Marvell) Ethernet switches can respond to special control
frames used to access the internal switch registers. This provides
an alternative control bus for the switches, in addition to SMI. This
is called Remote Management Unit (RMU) and must be activated on
choosen port(s). Here's an illustration of what we may see:

        +-----------+
        |    CPU    +-----+
        +----+ +----+     |             Control path via port 1.
        |eth0| |eth1|     | MDIO        Data path via port port 3.
        +-+--+-+--+-+     |(optional)   eth1 is a dedicated DSA master,
(optional)|       |       |             eth0 is a normal interface,
    +---+-+-+---+-+-+---+ |             doing normal traffic + MGMT.
    | 0 | 1 | 2 | 3 | 4 | | (optional)
    +---+---+---+---+---+ | +--------+  There can also be several
    |  Ethernet Switch  +-+ | EEPROM |  interconnected switches using
    +-----------------+-+   +-+------+  a single control interface.
                      +-------+

Working examples for RMU are the vf610-zii-dev boards where the Control
and Data paths are both using the single DSA master interface wired
to 2 or 3 interconnected switches.

Having access to both SMI and Ethernet busses in the driver is
interesting because they each have specific operations which can
be more efficient in certain scenarios. For example, SMI has an ATU
GetNext operation which is handy to lookup a particular FDB entry,
but is expensive for dumping the whole database. RMU has no ATU
GetNext operation, but a Dump ATU operation allowing to retrieve up
to 48 entries per frame. RMU also has a Wait Bit implementation and
statistics dump operations. Ideally the driver would choose which
bus to use depending on the operation.

This RFC only implements the hooks in DSA to allow a switch driver to
send and receive frames it is interested in, as well as the register
read and write operations through RMU in mv88e6xxx as an illustration.

Please do not spend too much time reviewing the frame crafting itself
(in rmu.c), which currently hardcodes values and must share code with
the (E)DSA taggers. This part will be polished later.

The purpose of this RFC is not to discuss the implementation of RMU
frames in mv88e6xxx, but to discuss the appropriate way to implement
such control bus in the kernel and allow switch drivers to use it.

The master of such Ethernet bus could be any network interface,
doing normal traffic plus control frames for the switch(es) it is
(directly or indirectly) connected to. A proper virtual Ethernet
bus allowing switches to be probed can be implemented, plus DTS
properties to describe which interface to use as a bus master and
on which switch port the remote management must be enabled. In the
meantime, this RFC hooks into DSA.

My concerns are how to properly add hooks to filter frames on the
receive path of any struct net_device (including but not necessarily
a DSA master)? Are there other device drivers out there making use
of multiple control bus at the same time? Is this implementation
sufficient for the moment?


Cheers,

Vivien Didelot (9):
  net: dsa: introduce dsa_master_find_switch
  net: dsa: allow switches to receive frames
  net: dsa: allow switches to transmit frames
  net: dsa: introduce dsa_is_upstream_port
  net: dsa: introduce dsa_to_master
  net: dsa: mv88e6xxx: add default bus operations
  net: dsa: mv88e6xxx: implement RMU enable
  net: dsa: mv88e6xxx: setup RMU port
  net: dsa: mv88e6xxx: setup RMU bus

 drivers/net/dsa/mv88e6xxx/Makefile  |   1 +
 drivers/net/dsa/mv88e6xxx/chip.c    |  43 +++-
 drivers/net/dsa/mv88e6xxx/chip.h    |  14 ++
 drivers/net/dsa/mv88e6xxx/global1.c |  56 +++++
 drivers/net/dsa/mv88e6xxx/global1.h |   2 +
 drivers/net/dsa/mv88e6xxx/rmu.c     | 314 ++++++++++++++++++++++++++++
 drivers/net/dsa/mv88e6xxx/rmu.h     |  34 +++
 drivers/net/dsa/mv88e6xxx/smi.c     |   3 +
 drivers/net/dsa/mv88e6xxx/smi.h     |  18 --
 include/net/dsa.h                   |  32 +++
 net/dsa/dsa2.c                      |   6 +
 net/dsa/dsa_priv.h                  |  19 +-
 net/dsa/switch.c                    |  15 ++
 net/dsa/tag_dsa.c                   |   6 +
 net/dsa/tag_edsa.c                  |   6 +
 15 files changed, 536 insertions(+), 33 deletions(-)
 create mode 100644 drivers/net/dsa/mv88e6xxx/rmu.c
 create mode 100644 drivers/net/dsa/mv88e6xxx/rmu.h

-- 
2.21.0


^ permalink raw reply	[flat|nested] 11+ messages in thread

* [RFC net-next 1/9] net: dsa: introduce dsa_master_find_switch
  2019-05-21 19:29 [RFC net-next 0/9] net: dsa: mv88e6xxx: add RMU support Vivien Didelot
@ 2019-05-21 19:29 ` Vivien Didelot
  2019-05-21 19:29 ` [RFC net-next 2/9] net: dsa: allow switches to receive frames Vivien Didelot
                   ` (7 subsequent siblings)
  8 siblings, 0 replies; 11+ messages in thread
From: Vivien Didelot @ 2019-05-21 19:29 UTC (permalink / raw)
  To: netdev
  Cc: cphealy, Vivien Didelot, Florian Fainelli, Andrew Lunn, David S. Miller

Introduce a DSA helper to find a dsa_switch structure by ID from a
master interface, that will be useful for frames with a switch ID
but no port ID.

Signed-off-by: Vivien Didelot <vivien.didelot@gmail.com>
---
 net/dsa/dsa_priv.h | 17 ++++++++++++-----
 1 file changed, 12 insertions(+), 5 deletions(-)

diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h
index 8f1222324646..2a8ee4c6adc5 100644
--- a/net/dsa/dsa_priv.h
+++ b/net/dsa/dsa_priv.h
@@ -103,18 +103,25 @@ int dsa_legacy_fdb_del(struct ndmsg *ndm, struct nlattr *tb[],
 int dsa_master_setup(struct net_device *dev, struct dsa_port *cpu_dp);
 void dsa_master_teardown(struct net_device *dev);
 
-static inline struct net_device *dsa_master_find_slave(struct net_device *dev,
-						       int device, int port)
+static inline struct dsa_switch *dsa_master_find_switch(struct net_device *dev,
+							int device)
 {
 	struct dsa_port *cpu_dp = dev->dsa_ptr;
 	struct dsa_switch_tree *dst = cpu_dp->dst;
-	struct dsa_switch *ds;
-	struct dsa_port *slave_port;
 
 	if (device < 0 || device >= DSA_MAX_SWITCHES)
 		return NULL;
 
-	ds = dst->ds[device];
+	return dst->ds[device];
+}
+
+static inline struct net_device *dsa_master_find_slave(struct net_device *dev,
+						       int device, int port)
+{
+	struct dsa_port *slave_port;
+	struct dsa_switch *ds;
+
+	ds = dsa_master_find_switch(dev, device);
 	if (!ds)
 		return NULL;
 
-- 
2.21.0


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [RFC net-next 2/9] net: dsa: allow switches to receive frames
  2019-05-21 19:29 [RFC net-next 0/9] net: dsa: mv88e6xxx: add RMU support Vivien Didelot
  2019-05-21 19:29 ` [RFC net-next 1/9] net: dsa: introduce dsa_master_find_switch Vivien Didelot
@ 2019-05-21 19:29 ` Vivien Didelot
  2019-05-21 19:29 ` [RFC net-next 3/9] net: dsa: allow switches to transmit frames Vivien Didelot
                   ` (6 subsequent siblings)
  8 siblings, 0 replies; 11+ messages in thread
From: Vivien Didelot @ 2019-05-21 19:29 UTC (permalink / raw)
  To: netdev
  Cc: cphealy, Vivien Didelot, Florian Fainelli, Andrew Lunn, David S. Miller

Add a rcv DSA switch operation to allow taggers to let the driver of
the source switch to process the received frame on its own.

At the moment, only DSA and EDSA taggers make use of this hook.

Signed-off-by: Vivien Didelot <vivien.didelot@gmail.com>
---
 include/net/dsa.h  | 5 +++++
 net/dsa/tag_dsa.c  | 6 ++++++
 net/dsa/tag_edsa.c | 6 ++++++
 3 files changed, 17 insertions(+)

diff --git a/include/net/dsa.h b/include/net/dsa.h
index 685294817712..027bb67ebaf7 100644
--- a/include/net/dsa.h
+++ b/include/net/dsa.h
@@ -543,6 +543,11 @@ struct dsa_switch_ops {
 	 */
 	netdev_tx_t (*port_deferred_xmit)(struct dsa_switch *ds, int port,
 					  struct sk_buff *skb);
+
+	/*
+         * SKB Rx hooks
+         */
+	bool (*rcv)(struct dsa_switch *ds, struct sk_buff *skb);
 };
 
 struct dsa_switch_driver {
diff --git a/net/dsa/tag_dsa.c b/net/dsa/tag_dsa.c
index 7ddec9794477..20688674e00f 100644
--- a/net/dsa/tag_dsa.c
+++ b/net/dsa/tag_dsa.c
@@ -63,6 +63,7 @@ static struct sk_buff *dsa_xmit(struct sk_buff *skb, struct net_device *dev)
 static struct sk_buff *dsa_rcv(struct sk_buff *skb, struct net_device *dev,
 			       struct packet_type *pt)
 {
+	struct dsa_switch *ds;
 	u8 *dsa_header;
 	int source_device;
 	int source_port;
@@ -87,6 +88,11 @@ static struct sk_buff *dsa_rcv(struct sk_buff *skb, struct net_device *dev,
 	source_device = dsa_header[0] & 0x1f;
 	source_port = (dsa_header[1] >> 3) & 0x1f;
 
+	/* Allow the source switch device to process the frame on its own */
+	ds = dsa_master_find_switch(dev, source_device);
+	if (ds && ds->ops->rcv && ds->ops->rcv(ds, skb))
+		return NULL;
+
 	skb->dev = dsa_master_find_slave(dev, source_device, source_port);
 	if (!skb->dev)
 		return NULL;
diff --git a/net/dsa/tag_edsa.c b/net/dsa/tag_edsa.c
index e8eaa804ccb9..2ba3d48a23e2 100644
--- a/net/dsa/tag_edsa.c
+++ b/net/dsa/tag_edsa.c
@@ -76,6 +76,7 @@ static struct sk_buff *edsa_xmit(struct sk_buff *skb, struct net_device *dev)
 static struct sk_buff *edsa_rcv(struct sk_buff *skb, struct net_device *dev,
 				struct packet_type *pt)
 {
+	struct dsa_switch *ds;
 	u8 *edsa_header;
 	int source_device;
 	int source_port;
@@ -100,6 +101,11 @@ static struct sk_buff *edsa_rcv(struct sk_buff *skb, struct net_device *dev,
 	source_device = edsa_header[0] & 0x1f;
 	source_port = (edsa_header[1] >> 3) & 0x1f;
 
+	/* Allow the target switch device to process the frame on its own */
+	ds = dsa_master_find_switch(dev, source_device);
+	if (ds && ds->ops->rcv && ds->ops->rcv(ds, skb))
+		return NULL;
+
 	skb->dev = dsa_master_find_slave(dev, source_device, source_port);
 	if (!skb->dev)
 		return NULL;
-- 
2.21.0


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [RFC net-next 3/9] net: dsa: allow switches to transmit frames
  2019-05-21 19:29 [RFC net-next 0/9] net: dsa: mv88e6xxx: add RMU support Vivien Didelot
  2019-05-21 19:29 ` [RFC net-next 1/9] net: dsa: introduce dsa_master_find_switch Vivien Didelot
  2019-05-21 19:29 ` [RFC net-next 2/9] net: dsa: allow switches to receive frames Vivien Didelot
@ 2019-05-21 19:29 ` Vivien Didelot
  2019-05-21 19:29 ` [RFC net-next 4/9] net: dsa: introduce dsa_is_upstream_port Vivien Didelot
                   ` (5 subsequent siblings)
  8 siblings, 0 replies; 11+ messages in thread
From: Vivien Didelot @ 2019-05-21 19:29 UTC (permalink / raw)
  To: netdev
  Cc: cphealy, Vivien Didelot, Florian Fainelli, Andrew Lunn, David S. Miller

Introduce an xmit and work queue to allow switches to send frames
on their own, which can be useful to implement control frames via
Ethernet for instance.

Signed-off-by: Vivien Didelot <vivien.didelot@gmail.com>
---
 include/net/dsa.h  |  5 +++++
 net/dsa/dsa2.c     |  6 ++++++
 net/dsa/dsa_priv.h |  2 ++
 net/dsa/switch.c   | 15 +++++++++++++++
 4 files changed, 28 insertions(+)

diff --git a/include/net/dsa.h b/include/net/dsa.h
index 027bb67ebaf7..7b10a067b06d 100644
--- a/include/net/dsa.h
+++ b/include/net/dsa.h
@@ -275,6 +275,9 @@ struct dsa_switch {
 	 */
 	bool			vlan_filtering;
 
+	struct work_struct	xmit_work;
+	struct sk_buff_head	xmit_queue;
+
 	unsigned long		*bitmap;
 	unsigned long		_bitmap;
 
@@ -283,6 +286,8 @@ struct dsa_switch {
 	struct dsa_port ports[];
 };
 
+void dsa_switch_xmit(struct dsa_switch *ds, struct sk_buff *skb);
+
 static inline const struct dsa_port *dsa_to_port(struct dsa_switch *ds, int p)
 {
 	return &ds->ports[p];
diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c
index 3b5f434cad3f..fb7318b20e0a 100644
--- a/net/dsa/dsa2.c
+++ b/net/dsa/dsa2.c
@@ -375,6 +375,9 @@ static int dsa_switch_setup(struct dsa_switch *ds)
 	if (err)
 		return err;
 
+	skb_queue_head_init(&ds->xmit_queue);
+	INIT_WORK(&ds->xmit_work, dsa_switch_xmit_work);
+
 	err = ds->ops->setup(ds);
 	if (err < 0)
 		return err;
@@ -399,6 +402,9 @@ static void dsa_switch_teardown(struct dsa_switch *ds)
 	if (ds->slave_mii_bus && ds->ops->phy_read)
 		mdiobus_unregister(ds->slave_mii_bus);
 
+	cancel_work_sync(&ds->xmit_work);
+	skb_queue_purge(&ds->xmit_queue);
+
 	dsa_switch_unregister_notifier(ds);
 
 	if (ds->devlink) {
diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h
index 2a8ee4c6adc5..0ccb0eab6295 100644
--- a/net/dsa/dsa_priv.h
+++ b/net/dsa/dsa_priv.h
@@ -201,4 +201,6 @@ dsa_slave_to_master(const struct net_device *dev)
 /* switch.c */
 int dsa_switch_register_notifier(struct dsa_switch *ds);
 void dsa_switch_unregister_notifier(struct dsa_switch *ds);
+void dsa_switch_xmit_work(struct work_struct *work);
+
 #endif
diff --git a/net/dsa/switch.c b/net/dsa/switch.c
index 7d8cd9bc0ecc..b39d246f0d55 100644
--- a/net/dsa/switch.c
+++ b/net/dsa/switch.c
@@ -371,3 +371,18 @@ void dsa_switch_unregister_notifier(struct dsa_switch *ds)
 	if (err)
 		dev_err(ds->dev, "failed to unregister notifier (%d)\n", err);
 }
+
+void dsa_switch_xmit(struct dsa_switch *ds, struct sk_buff *skb)
+{
+	skb_queue_tail(&ds->xmit_queue, skb);
+	schedule_work(&ds->xmit_work);
+}
+
+void dsa_switch_xmit_work(struct work_struct *work)
+{
+	struct dsa_switch *ds = container_of(work, struct dsa_switch, xmit_work);
+	struct sk_buff *skb;
+
+	while ((skb = skb_dequeue(&ds->xmit_queue)) != NULL)
+		dev_queue_xmit(skb);
+}
-- 
2.21.0


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [RFC net-next 4/9] net: dsa: introduce dsa_is_upstream_port
  2019-05-21 19:29 [RFC net-next 0/9] net: dsa: mv88e6xxx: add RMU support Vivien Didelot
                   ` (2 preceding siblings ...)
  2019-05-21 19:29 ` [RFC net-next 3/9] net: dsa: allow switches to transmit frames Vivien Didelot
@ 2019-05-21 19:29 ` Vivien Didelot
  2019-05-21 19:30 ` [RFC net-next 5/9] net: dsa: introduce dsa_to_master Vivien Didelot
                   ` (4 subsequent siblings)
  8 siblings, 0 replies; 11+ messages in thread
From: Vivien Didelot @ 2019-05-21 19:29 UTC (permalink / raw)
  To: netdev
  Cc: cphealy, Vivien Didelot, Florian Fainelli, Andrew Lunn, David S. Miller

Introduce a dsa_is_upstream_port helper to check if a given switch
port is directly or indirectly connected to a CPU port.

Signed-off-by: Vivien Didelot <vivien.didelot@gmail.com>
---
 include/net/dsa.h | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/include/net/dsa.h b/include/net/dsa.h
index 7b10a067b06d..c5b45bfeea01 100644
--- a/include/net/dsa.h
+++ b/include/net/dsa.h
@@ -347,6 +347,15 @@ static inline unsigned int dsa_upstream_port(struct dsa_switch *ds, int port)
 	return dsa_towards_port(ds, cpu_dp->ds->index, cpu_dp->index);
 }
 
+/* Return whether the port is a local upstream port */
+static inline bool dsa_is_upstream_port(struct dsa_switch *ds, int port)
+{
+	if (dsa_is_unused_port(ds, port))
+		return false;
+
+	return dsa_upstream_port(ds, port) == port;
+}
+
 static inline bool dsa_port_is_vlan_filtering(const struct dsa_port *dp)
 {
 	const struct dsa_switch *ds = dp->ds;
-- 
2.21.0


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [RFC net-next 5/9] net: dsa: introduce dsa_to_master
  2019-05-21 19:29 [RFC net-next 0/9] net: dsa: mv88e6xxx: add RMU support Vivien Didelot
                   ` (3 preceding siblings ...)
  2019-05-21 19:29 ` [RFC net-next 4/9] net: dsa: introduce dsa_is_upstream_port Vivien Didelot
@ 2019-05-21 19:30 ` Vivien Didelot
  2019-05-21 19:30 ` [RFC net-next 6/9] net: dsa: mv88e6xxx: add default bus operations Vivien Didelot
                   ` (3 subsequent siblings)
  8 siblings, 0 replies; 11+ messages in thread
From: Vivien Didelot @ 2019-05-21 19:30 UTC (permalink / raw)
  To: netdev
  Cc: cphealy, Vivien Didelot, Florian Fainelli, Andrew Lunn, David S. Miller

Introduce a dsa_to_master helper to find the master interface dedicated
to a given switch port.

Signed-off-by: Vivien Didelot <vivien.didelot@gmail.com>
---
 include/net/dsa.h | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/include/net/dsa.h b/include/net/dsa.h
index c5b45bfeea01..b0be2687bd61 100644
--- a/include/net/dsa.h
+++ b/include/net/dsa.h
@@ -356,6 +356,19 @@ static inline bool dsa_is_upstream_port(struct dsa_switch *ds, int port)
 	return dsa_upstream_port(ds, port) == port;
 }
 
+static inline struct net_device *dsa_to_master(struct dsa_switch *ds, int port)
+{
+	const struct dsa_port *dp = dsa_to_port(ds, port);
+
+	if (dp->type == DSA_PORT_TYPE_CPU)
+		return dp->master;
+
+	if (dp->cpu_dp)
+		return dp->cpu_dp->master;
+
+	return NULL;
+}
+
 static inline bool dsa_port_is_vlan_filtering(const struct dsa_port *dp)
 {
 	const struct dsa_switch *ds = dp->ds;
-- 
2.21.0


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [RFC net-next 6/9] net: dsa: mv88e6xxx: add default bus operations
  2019-05-21 19:29 [RFC net-next 0/9] net: dsa: mv88e6xxx: add RMU support Vivien Didelot
                   ` (4 preceding siblings ...)
  2019-05-21 19:30 ` [RFC net-next 5/9] net: dsa: introduce dsa_to_master Vivien Didelot
@ 2019-05-21 19:30 ` Vivien Didelot
  2019-05-21 19:30 ` [RFC net-next 7/9] net: dsa: mv88e6xxx: implement RMU enable Vivien Didelot
                   ` (2 subsequent siblings)
  8 siblings, 0 replies; 11+ messages in thread
From: Vivien Didelot @ 2019-05-21 19:30 UTC (permalink / raw)
  To: netdev
  Cc: cphealy, Vivien Didelot, Florian Fainelli, Andrew Lunn, David S. Miller

In order to prepare the introduction of alternative busses, add a
default mv88e6xxx_bus_ops pointer to the mv88e6xxx_chip structure.

A bus may set the default operations if they aren't already set.

Signed-off-by: Vivien Didelot <vivien.didelot@gmail.com>
---
 drivers/net/dsa/mv88e6xxx/chip.c | 10 ++++++++--
 drivers/net/dsa/mv88e6xxx/chip.h |  3 +++
 drivers/net/dsa/mv88e6xxx/smi.c  |  3 +++
 drivers/net/dsa/mv88e6xxx/smi.h  | 18 ------------------
 4 files changed, 14 insertions(+), 20 deletions(-)

diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c
index 28414db979b0..96e1886e05f0 100644
--- a/drivers/net/dsa/mv88e6xxx/chip.c
+++ b/drivers/net/dsa/mv88e6xxx/chip.c
@@ -59,7 +59,10 @@ int mv88e6xxx_read(struct mv88e6xxx_chip *chip, int addr, int reg, u16 *val)
 
 	assert_reg_lock(chip);
 
-	err = mv88e6xxx_smi_read(chip, addr, reg, val);
+	if (unlikely(!(chip->ops && chip->ops->read)))
+		return -EOPNOTSUPP;
+
+	err = chip->ops->read(chip, addr, reg, val);
 	if (err)
 		return err;
 
@@ -75,7 +78,10 @@ int mv88e6xxx_write(struct mv88e6xxx_chip *chip, int addr, int reg, u16 val)
 
 	assert_reg_lock(chip);
 
-	err = mv88e6xxx_smi_write(chip, addr, reg, val);
+	if (unlikely(!(chip->ops && chip->ops->write)))
+		return -EOPNOTSUPP;
+
+	err = chip->ops->write(chip, addr, reg, val);
 	if (err)
 		return err;
 
diff --git a/drivers/net/dsa/mv88e6xxx/chip.h b/drivers/net/dsa/mv88e6xxx/chip.h
index faa3fa889f19..860816ebb7ee 100644
--- a/drivers/net/dsa/mv88e6xxx/chip.h
+++ b/drivers/net/dsa/mv88e6xxx/chip.h
@@ -204,6 +204,9 @@ struct mv88e6xxx_chip {
 	/* This mutex protects the access to the switch registers */
 	struct mutex reg_lock;
 
+	/* The default registered bus operations */
+	const struct mv88e6xxx_bus_ops *ops;
+
 	/* The MII bus and the address on the bus that is used to
 	 * communication with the switch
 	 */
diff --git a/drivers/net/dsa/mv88e6xxx/smi.c b/drivers/net/dsa/mv88e6xxx/smi.c
index 96f7d2685bdc..77c40596b678 100644
--- a/drivers/net/dsa/mv88e6xxx/smi.c
+++ b/drivers/net/dsa/mv88e6xxx/smi.c
@@ -154,5 +154,8 @@ int mv88e6xxx_smi_init(struct mv88e6xxx_chip *chip,
 	chip->bus = bus;
 	chip->sw_addr = sw_addr;
 
+	if (!chip->ops)
+		chip->ops = chip->smi_ops;
+
 	return 0;
 }
diff --git a/drivers/net/dsa/mv88e6xxx/smi.h b/drivers/net/dsa/mv88e6xxx/smi.h
index 35e6403b65dc..566bfa174354 100644
--- a/drivers/net/dsa/mv88e6xxx/smi.h
+++ b/drivers/net/dsa/mv88e6xxx/smi.h
@@ -38,22 +38,4 @@
 int mv88e6xxx_smi_init(struct mv88e6xxx_chip *chip,
 		       struct mii_bus *bus, int sw_addr);
 
-static inline int mv88e6xxx_smi_read(struct mv88e6xxx_chip *chip,
-				     int dev, int reg, u16 *data)
-{
-	if (chip->smi_ops && chip->smi_ops->read)
-		return chip->smi_ops->read(chip, dev, reg, data);
-
-	return -EOPNOTSUPP;
-}
-
-static inline int mv88e6xxx_smi_write(struct mv88e6xxx_chip *chip,
-				      int dev, int reg, u16 data)
-{
-	if (chip->smi_ops && chip->smi_ops->write)
-		return chip->smi_ops->write(chip, dev, reg, data);
-
-	return -EOPNOTSUPP;
-}
-
 #endif /* _MV88E6XXX_SMI_H */
-- 
2.21.0


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [RFC net-next 7/9] net: dsa: mv88e6xxx: implement RMU enable
  2019-05-21 19:29 [RFC net-next 0/9] net: dsa: mv88e6xxx: add RMU support Vivien Didelot
                   ` (5 preceding siblings ...)
  2019-05-21 19:30 ` [RFC net-next 6/9] net: dsa: mv88e6xxx: add default bus operations Vivien Didelot
@ 2019-05-21 19:30 ` Vivien Didelot
  2019-05-21 19:30 ` [RFC net-next 8/9] net: dsa: mv88e6xxx: setup RMU port Vivien Didelot
  2019-05-21 19:30 ` [RFC net-next 9/9] net: dsa: mv88e6xxx: setup RMU bus Vivien Didelot
  8 siblings, 0 replies; 11+ messages in thread
From: Vivien Didelot @ 2019-05-21 19:30 UTC (permalink / raw)
  To: netdev
  Cc: cphealy, Vivien Didelot, Florian Fainelli, Andrew Lunn, David S. Miller

Implement a new operation to enable the Remote Management Unit (RMU)
on a specified port. Add such support for 88E6352 and 88E6390 switches.

Signed-off-by: Vivien Didelot <vivien.didelot@gmail.com>
---
 drivers/net/dsa/mv88e6xxx/chip.c    | 10 ++++++
 drivers/net/dsa/mv88e6xxx/chip.h    |  1 +
 drivers/net/dsa/mv88e6xxx/global1.c | 56 +++++++++++++++++++++++++++++
 drivers/net/dsa/mv88e6xxx/global1.h |  2 ++
 4 files changed, 69 insertions(+)

diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c
index 96e1886e05f0..3aa2b315b96d 100644
--- a/drivers/net/dsa/mv88e6xxx/chip.c
+++ b/drivers/net/dsa/mv88e6xxx/chip.c
@@ -3142,6 +3142,7 @@ static const struct mv88e6xxx_ops mv88e6172_ops = {
 	.mgmt_rsvd2cpu = mv88e6352_g2_mgmt_rsvd2cpu,
 	.pot_clear = mv88e6xxx_g2_pot_clear,
 	.reset = mv88e6352_g1_reset,
+	.rmu_enable = mv88e6352_g1_rmu_enable,
 	.rmu_disable = mv88e6352_g1_rmu_disable,
 	.vtu_getnext = mv88e6352_g1_vtu_getnext,
 	.vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
@@ -3225,6 +3226,7 @@ static const struct mv88e6xxx_ops mv88e6176_ops = {
 	.mgmt_rsvd2cpu = mv88e6352_g2_mgmt_rsvd2cpu,
 	.pot_clear = mv88e6xxx_g2_pot_clear,
 	.reset = mv88e6352_g1_reset,
+	.rmu_enable = mv88e6352_g1_rmu_enable,
 	.rmu_disable = mv88e6352_g1_rmu_disable,
 	.vtu_getnext = mv88e6352_g1_vtu_getnext,
 	.vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
@@ -3305,6 +3307,7 @@ static const struct mv88e6xxx_ops mv88e6190_ops = {
 	.mgmt_rsvd2cpu = mv88e6390_g1_mgmt_rsvd2cpu,
 	.pot_clear = mv88e6xxx_g2_pot_clear,
 	.reset = mv88e6352_g1_reset,
+	.rmu_enable = mv88e6390_g1_rmu_enable,
 	.rmu_disable = mv88e6390_g1_rmu_disable,
 	.vtu_getnext = mv88e6390_g1_vtu_getnext,
 	.vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,
@@ -3350,6 +3353,7 @@ static const struct mv88e6xxx_ops mv88e6190x_ops = {
 	.mgmt_rsvd2cpu = mv88e6390_g1_mgmt_rsvd2cpu,
 	.pot_clear = mv88e6xxx_g2_pot_clear,
 	.reset = mv88e6352_g1_reset,
+	.rmu_enable = mv88e6390_g1_rmu_enable,
 	.rmu_disable = mv88e6390_g1_rmu_disable,
 	.vtu_getnext = mv88e6390_g1_vtu_getnext,
 	.vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,
@@ -3395,6 +3399,7 @@ static const struct mv88e6xxx_ops mv88e6191_ops = {
 	.mgmt_rsvd2cpu = mv88e6390_g1_mgmt_rsvd2cpu,
 	.pot_clear = mv88e6xxx_g2_pot_clear,
 	.reset = mv88e6352_g1_reset,
+	.rmu_enable = mv88e6390_g1_rmu_enable,
 	.rmu_disable = mv88e6390_g1_rmu_disable,
 	.vtu_getnext = mv88e6390_g1_vtu_getnext,
 	.vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,
@@ -3442,6 +3447,7 @@ static const struct mv88e6xxx_ops mv88e6240_ops = {
 	.mgmt_rsvd2cpu = mv88e6352_g2_mgmt_rsvd2cpu,
 	.pot_clear = mv88e6xxx_g2_pot_clear,
 	.reset = mv88e6352_g1_reset,
+	.rmu_enable = mv88e6352_g1_rmu_enable,
 	.rmu_disable = mv88e6352_g1_rmu_disable,
 	.vtu_getnext = mv88e6352_g1_vtu_getnext,
 	.vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
@@ -3489,6 +3495,7 @@ static const struct mv88e6xxx_ops mv88e6290_ops = {
 	.mgmt_rsvd2cpu = mv88e6390_g1_mgmt_rsvd2cpu,
 	.pot_clear = mv88e6xxx_g2_pot_clear,
 	.reset = mv88e6352_g1_reset,
+	.rmu_enable = mv88e6390_g1_rmu_enable,
 	.rmu_disable = mv88e6390_g1_rmu_disable,
 	.vtu_getnext = mv88e6390_g1_vtu_getnext,
 	.vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,
@@ -3747,6 +3754,7 @@ static const struct mv88e6xxx_ops mv88e6352_ops = {
 	.mgmt_rsvd2cpu = mv88e6352_g2_mgmt_rsvd2cpu,
 	.pot_clear = mv88e6xxx_g2_pot_clear,
 	.reset = mv88e6352_g1_reset,
+	.rmu_enable = mv88e6352_g1_rmu_enable,
 	.rmu_disable = mv88e6352_g1_rmu_disable,
 	.vtu_getnext = mv88e6352_g1_vtu_getnext,
 	.vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
@@ -3799,6 +3807,7 @@ static const struct mv88e6xxx_ops mv88e6390_ops = {
 	.mgmt_rsvd2cpu = mv88e6390_g1_mgmt_rsvd2cpu,
 	.pot_clear = mv88e6xxx_g2_pot_clear,
 	.reset = mv88e6352_g1_reset,
+	.rmu_enable = mv88e6390_g1_rmu_enable,
 	.rmu_disable = mv88e6390_g1_rmu_disable,
 	.vtu_getnext = mv88e6390_g1_vtu_getnext,
 	.vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,
@@ -3848,6 +3857,7 @@ static const struct mv88e6xxx_ops mv88e6390x_ops = {
 	.mgmt_rsvd2cpu = mv88e6390_g1_mgmt_rsvd2cpu,
 	.pot_clear = mv88e6xxx_g2_pot_clear,
 	.reset = mv88e6352_g1_reset,
+	.rmu_enable = mv88e6390_g1_rmu_enable,
 	.rmu_disable = mv88e6390_g1_rmu_disable,
 	.vtu_getnext = mv88e6390_g1_vtu_getnext,
 	.vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,
diff --git a/drivers/net/dsa/mv88e6xxx/chip.h b/drivers/net/dsa/mv88e6xxx/chip.h
index 860816ebb7ee..2af574169e14 100644
--- a/drivers/net/dsa/mv88e6xxx/chip.h
+++ b/drivers/net/dsa/mv88e6xxx/chip.h
@@ -459,6 +459,7 @@ struct mv88e6xxx_ops {
 	const struct mv88e6xxx_avb_ops *avb_ops;
 
 	/* Remote Management Unit operations */
+	int (*rmu_enable)(struct mv88e6xxx_chip *chip, int port, bool da_check);
 	int (*rmu_disable)(struct mv88e6xxx_chip *chip);
 
 	/* Precision Time Protocol operations */
diff --git a/drivers/net/dsa/mv88e6xxx/global1.c b/drivers/net/dsa/mv88e6xxx/global1.c
index 38e399e0f30e..523a7e297577 100644
--- a/drivers/net/dsa/mv88e6xxx/global1.c
+++ b/drivers/net/dsa/mv88e6xxx/global1.c
@@ -443,12 +443,68 @@ int mv88e6352_g1_rmu_disable(struct mv88e6xxx_chip *chip)
 				      MV88E6352_G1_CTL2_RMU_MODE_DISABLED);
 }
 
+int mv88e6352_g1_rmu_enable(struct mv88e6xxx_chip *chip, int port, bool da_check)
+{
+	u16 mask = MV88E6352_G1_CTL2_RMU_MODE_MASK | MV88E6352_G1_CTL2_DA_CHECK;
+	u16 val;
+
+	switch (port) {
+	case 4:
+		val = MV88E6352_G1_CTL2_RMU_MODE_PORT_4;
+		break;
+	case 5:
+		val = MV88E6352_G1_CTL2_RMU_MODE_PORT_5;
+		break;
+	case 6:
+		val = MV88E6352_G1_CTL2_RMU_MODE_PORT_6;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (da_check)
+		val |= MV88E6352_G1_CTL2_DA_CHECK;
+
+	return mv88e6xxx_g1_ctl2_mask(chip, mask, val);
+}
+
 int mv88e6390_g1_rmu_disable(struct mv88e6xxx_chip *chip)
 {
 	return mv88e6xxx_g1_ctl2_mask(chip, MV88E6390_G1_CTL2_RMU_MODE_MASK,
 				      MV88E6390_G1_CTL2_RMU_MODE_DISABLED);
 }
 
+int mv88e6390_g1_rmu_enable(struct mv88e6xxx_chip *chip, int port, bool da_check)
+{
+	u16 mask = MV88E6390_G1_CTL2_RMU_MODE_MASK | MV88E6352_G1_CTL2_DA_CHECK;
+	u16 val;
+
+	switch (port) {
+	case 0:
+		val = MV88E6390_G1_CTL2_RMU_MODE_PORT_0;
+		break;
+	case 1:
+		val = MV88E6390_G1_CTL2_RMU_MODE_PORT_1;
+		break;
+	case 9:
+		val = MV88E6390_G1_CTL2_RMU_MODE_PORT_9;
+		break;
+	case 10:
+		val = MV88E6390_G1_CTL2_RMU_MODE_PORT_10;
+		break;
+	default:
+		/* 88E6390X can enable RMU on all (E)DSA ports as well,
+                 * but let's request a specific port for the moment.
+                 */
+		return -EINVAL;
+	}
+
+	if (da_check)
+		val |= MV88E6352_G1_CTL2_DA_CHECK;
+
+	return mv88e6xxx_g1_ctl2_mask(chip, mask, val);
+}
+
 int mv88e6390_g1_stats_set_histogram(struct mv88e6xxx_chip *chip)
 {
 	return mv88e6xxx_g1_ctl2_mask(chip, MV88E6390_G1_CTL2_HIST_MODE_MASK,
diff --git a/drivers/net/dsa/mv88e6xxx/global1.h b/drivers/net/dsa/mv88e6xxx/global1.h
index bef01331266f..c6057cdd547f 100644
--- a/drivers/net/dsa/mv88e6xxx/global1.h
+++ b/drivers/net/dsa/mv88e6xxx/global1.h
@@ -283,7 +283,9 @@ int mv88e6085_g1_ieee_pri_map(struct mv88e6xxx_chip *chip);
 int mv88e6185_g1_set_cascade_port(struct mv88e6xxx_chip *chip, int port);
 
 int mv88e6085_g1_rmu_disable(struct mv88e6xxx_chip *chip);
+int mv88e6352_g1_rmu_enable(struct mv88e6xxx_chip *chip, int port, bool da_check);
 int mv88e6352_g1_rmu_disable(struct mv88e6xxx_chip *chip);
+int mv88e6390_g1_rmu_enable(struct mv88e6xxx_chip *chip, int port, bool da_check);
 int mv88e6390_g1_rmu_disable(struct mv88e6xxx_chip *chip);
 
 int mv88e6xxx_g1_set_device_number(struct mv88e6xxx_chip *chip, int index);
-- 
2.21.0


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [RFC net-next 8/9] net: dsa: mv88e6xxx: setup RMU port
  2019-05-21 19:29 [RFC net-next 0/9] net: dsa: mv88e6xxx: add RMU support Vivien Didelot
                   ` (6 preceding siblings ...)
  2019-05-21 19:30 ` [RFC net-next 7/9] net: dsa: mv88e6xxx: implement RMU enable Vivien Didelot
@ 2019-05-21 19:30 ` Vivien Didelot
  2019-05-21 19:30 ` [RFC net-next 9/9] net: dsa: mv88e6xxx: setup RMU bus Vivien Didelot
  8 siblings, 0 replies; 11+ messages in thread
From: Vivien Didelot @ 2019-05-21 19:30 UTC (permalink / raw)
  To: netdev
  Cc: cphealy, Vivien Didelot, Florian Fainelli, Andrew Lunn, David S. Miller

Enable the RMU on the first upstream port found on this switch.

Signed-off-by: Vivien Didelot <vivien.didelot@gmail.com>
---
 drivers/net/dsa/mv88e6xxx/Makefile |  1 +
 drivers/net/dsa/mv88e6xxx/chip.c   |  9 +----
 drivers/net/dsa/mv88e6xxx/rmu.c    | 58 ++++++++++++++++++++++++++++++
 drivers/net/dsa/mv88e6xxx/rmu.h    | 33 +++++++++++++++++
 4 files changed, 93 insertions(+), 8 deletions(-)
 create mode 100644 drivers/net/dsa/mv88e6xxx/rmu.c
 create mode 100644 drivers/net/dsa/mv88e6xxx/rmu.h

diff --git a/drivers/net/dsa/mv88e6xxx/Makefile b/drivers/net/dsa/mv88e6xxx/Makefile
index e85755dde90b..c95679d0c615 100644
--- a/drivers/net/dsa/mv88e6xxx/Makefile
+++ b/drivers/net/dsa/mv88e6xxx/Makefile
@@ -11,5 +11,6 @@ mv88e6xxx-$(CONFIG_NET_DSA_MV88E6XXX_PTP) += hwtstamp.o
 mv88e6xxx-objs += phy.o
 mv88e6xxx-objs += port.o
 mv88e6xxx-$(CONFIG_NET_DSA_MV88E6XXX_PTP) += ptp.o
+mv88e6xxx-objs += rmu.o
 mv88e6xxx-objs += serdes.o
 mv88e6xxx-objs += smi.o
diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c
index 3aa2b315b96d..048fdaf1335e 100644
--- a/drivers/net/dsa/mv88e6xxx/chip.c
+++ b/drivers/net/dsa/mv88e6xxx/chip.c
@@ -42,6 +42,7 @@
 #include "phy.h"
 #include "port.h"
 #include "ptp.h"
+#include "rmu.h"
 #include "serdes.h"
 #include "smi.h"
 
@@ -1199,14 +1200,6 @@ static int mv88e6xxx_trunk_setup(struct mv88e6xxx_chip *chip)
 	return 0;
 }
 
-static int mv88e6xxx_rmu_setup(struct mv88e6xxx_chip *chip)
-{
-	if (chip->info->ops->rmu_disable)
-		return chip->info->ops->rmu_disable(chip);
-
-	return 0;
-}
-
 static int mv88e6xxx_pot_setup(struct mv88e6xxx_chip *chip)
 {
 	if (chip->info->ops->pot_clear)
diff --git a/drivers/net/dsa/mv88e6xxx/rmu.c b/drivers/net/dsa/mv88e6xxx/rmu.c
new file mode 100644
index 000000000000..71dabe6ecb46
--- /dev/null
+++ b/drivers/net/dsa/mv88e6xxx/rmu.c
@@ -0,0 +1,58 @@
+/*
+ * Marvell 88E6xxx Remote Management Unit (RMU) support
+ *
+ * Copyright (c) 2019 Vivien Didelot <vivien.didelot@gmail.com>
+ *
+ * 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 "chip.h"
+#include "rmu.h"
+
+static int mv88e6xxx_rmu_setup_port(struct mv88e6xxx_chip *chip, int port)
+{
+	int err;
+
+	/* First disable the RMU */
+	if (chip->info->ops->rmu_disable) {
+		err = chip->info->ops->rmu_disable(chip);
+		if (err)
+			return err;
+	}
+
+	/* Then enable the RMU on this dedicated port */
+	if (chip->info->ops->rmu_enable) {
+		err = chip->info->ops->rmu_enable(chip, port, false);
+		if (err)
+			return err;
+
+		dev_info(chip->dev, "RMU enabled on port %d\n", port);
+
+		return 0;
+	}
+
+	return -EOPNOTSUPP;
+}
+
+int mv88e6xxx_rmu_setup(struct mv88e6xxx_chip *chip)
+{
+	struct dsa_switch *ds = chip->ds;
+	int port;
+	int err;
+
+	/* Find a local port (in)directly connected to the CPU to enable RMU on */
+	for (port = 0; port < mv88e6xxx_num_ports(chip); port++) {
+		if (dsa_is_upstream_port(ds, port)) {
+			err = mv88e6xxx_rmu_setup_port(chip, port);
+			if (err)
+				continue;
+
+			return 0;
+		}
+	}
+
+	return 0;
+}
diff --git a/drivers/net/dsa/mv88e6xxx/rmu.h b/drivers/net/dsa/mv88e6xxx/rmu.h
new file mode 100644
index 000000000000..f7d849b169d2
--- /dev/null
+++ b/drivers/net/dsa/mv88e6xxx/rmu.h
@@ -0,0 +1,33 @@
+/*
+ * Marvell 88E6xxx System Management Interface (RMU) support
+ *
+ * Copyright (c) 2019 Vivien Didelot <vivien.didelot@gmail.com>
+ *
+ * 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_RMU_H
+#define _MV88E6XXX_RMU_H
+
+#define MV88E6XXX_RMU_REQUEST_FORMAT_SOHO	0x0001
+
+#define MV88E6XXX_RMU_REQUEST_CODE_GET_ID	0x0000
+#define MV88E6XXX_RMU_REQUEST_CODE_DUMP_ATU	0x1000
+#define MV88E6XXX_RMU_REQUEST_CODE_DUMP_MIB	0x1020
+#define MV88E6XXX_RMU_REQUEST_CODE_READ_WRITE	0x2000
+
+#define MV88E6XXX_RMU_REQUEST_DATA_DUMP_MIB_CLEAR	0x8000
+
+#define MV88E6XXX_RMU_RESPONSE_CODE_GET_ID	MV88E6XXX_RMU_REQUEST_CODE_GET_ID
+#define MV88E6XXX_RMU_RESPONSE_CODE_DUMP_ATU	MV88E6XXX_RMU_REQUEST_CODE_DUMP_ATU
+#define MV88E6XXX_RMU_RESPONSE_CODE_DUMP_MIB	MV88E6XXX_RMU_REQUEST_CODE_DUMP_MIB
+#define MV88E6XXX_RMU_RESPONSE_CODE_READ_WRITE	MV88E6XXX_RMU_REQUEST_CODE_READ_WRITE
+
+#include "chip.h"
+
+int mv88e6xxx_rmu_setup(struct mv88e6xxx_chip *chip);
+
+#endif /* _MV88E6XXX_RMU_H */
-- 
2.21.0


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [RFC net-next 9/9] net: dsa: mv88e6xxx: setup RMU bus
  2019-05-21 19:29 [RFC net-next 0/9] net: dsa: mv88e6xxx: add RMU support Vivien Didelot
                   ` (7 preceding siblings ...)
  2019-05-21 19:30 ` [RFC net-next 8/9] net: dsa: mv88e6xxx: setup RMU port Vivien Didelot
@ 2019-05-21 19:30 ` Vivien Didelot
  2019-05-21 20:42   ` Andrew Lunn
  8 siblings, 1 reply; 11+ messages in thread
From: Vivien Didelot @ 2019-05-21 19:30 UTC (permalink / raw)
  To: netdev
  Cc: cphealy, Vivien Didelot, Florian Fainelli, Andrew Lunn, David S. Miller

Implement the RMU register operations Read and Write and setup a
proper bus to use as an alternative for SMI.

Signed-off-by: Vivien Didelot <vivien.didelot@gmail.com>
---
 drivers/net/dsa/mv88e6xxx/chip.c |  14 ++
 drivers/net/dsa/mv88e6xxx/chip.h |  10 ++
 drivers/net/dsa/mv88e6xxx/rmu.c  | 256 +++++++++++++++++++++++++++++++
 drivers/net/dsa/mv88e6xxx/rmu.h  |   1 +
 4 files changed, 281 insertions(+)

diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c
index 048fdaf1335e..68c247e951aa 100644
--- a/drivers/net/dsa/mv88e6xxx/chip.c
+++ b/drivers/net/dsa/mv88e6xxx/chip.c
@@ -4573,6 +4573,19 @@ static int mv88e6xxx_port_egress_floods(struct dsa_switch *ds, int port,
 	return err;
 }
 
+static bool mv88e6xxx_rcv(struct dsa_switch *ds, struct sk_buff *skb)
+{
+	struct mv88e6xxx_chip *chip = ds->priv;
+
+	/* When this operation is called after a request,
+	 * the mutex must already be held.
+	 */
+	if (mutex_is_locked(&chip->reg_lock))
+		return !!mv88e6xxx_rmu_response(chip, skb);
+
+	return false;
+}
+
 static const struct dsa_switch_ops mv88e6xxx_switch_ops = {
 	.get_tag_protocol	= mv88e6xxx_get_tag_protocol,
 	.setup			= mv88e6xxx_setup,
@@ -4617,6 +4630,7 @@ static const struct dsa_switch_ops mv88e6xxx_switch_ops = {
 	.port_txtstamp		= mv88e6xxx_port_txtstamp,
 	.port_rxtstamp		= mv88e6xxx_port_rxtstamp,
 	.get_ts_info		= mv88e6xxx_get_ts_info,
+	.rcv			= mv88e6xxx_rcv,
 };
 
 static int mv88e6xxx_register_switch(struct mv88e6xxx_chip *chip)
diff --git a/drivers/net/dsa/mv88e6xxx/chip.h b/drivers/net/dsa/mv88e6xxx/chip.h
index 2af574169e14..bf328cea67c8 100644
--- a/drivers/net/dsa/mv88e6xxx/chip.h
+++ b/drivers/net/dsa/mv88e6xxx/chip.h
@@ -12,6 +12,7 @@
 #ifndef _MV88E6XXX_CHIP_H
 #define _MV88E6XXX_CHIP_H
 
+#include <linux/completion.h>
 #include <linux/if_vlan.h>
 #include <linux/irq.h>
 #include <linux/gpio/consumer.h>
@@ -214,6 +215,15 @@ struct mv88e6xxx_chip {
 	struct mii_bus *bus;
 	int sw_addr;
 
+	/* Register access through the Remote Management Unit */
+	const struct mv88e6xxx_bus_ops *rmu_ops;
+	struct completion rmu_response_received;
+	const unsigned char *rmu_response_data;
+	size_t rmu_response_data_len;
+	struct sk_buff *rmu_response;
+	struct net_device *rmu_dev;
+	u8 rmu_sequence_num;
+
 	/* Handles automatic disabling and re-enabling of the PHY
 	 * polling unit.
 	 */
diff --git a/drivers/net/dsa/mv88e6xxx/rmu.c b/drivers/net/dsa/mv88e6xxx/rmu.c
index 71dabe6ecb46..b7dbd843753a 100644
--- a/drivers/net/dsa/mv88e6xxx/rmu.c
+++ b/drivers/net/dsa/mv88e6xxx/rmu.c
@@ -9,9 +9,255 @@
  * (at your option) any later version.
  */
 
+#include <linux/if_ether.h>
+
 #include "chip.h"
 #include "rmu.h"
 
+#define MV88E6XXX_RMU_TIMEOUT (msecs_to_jiffies(1000))
+
+static int mv88e6xxx_rmu_wait_response(struct mv88e6xxx_chip *chip)
+{
+	long timeout;
+
+	timeout = wait_for_completion_interruptible_timeout(&chip->rmu_response_received, MV88E6XXX_RMU_TIMEOUT);
+	if (timeout < 0)
+		return timeout;
+	if (timeout == 0)
+		return -ETIMEDOUT;
+
+	dev_dbg(chip->dev, "got RMU response for request %d in %d msecs\n",
+		chip->rmu_sequence_num, jiffies_to_msecs(MV88E6XXX_RMU_TIMEOUT - timeout));
+
+	return 0;
+}
+
+#define DSA_LEN		4
+
+struct edsahdr {
+	unsigned char	eth_dest_addr[ETH_ALEN];
+	unsigned char	eth_src_addr[ETH_ALEN];
+	__be16		edsa_ethertype;
+	__be16		edsa_reserved; /* 0x0000 */
+	unsigned char	dsa_tag[DSA_LEN];
+	__be16		eth_ethertype;
+} __attribute__((packed));
+
+struct dsahdr {
+	unsigned char	eth_dest_addr[ETH_ALEN];
+	unsigned char	eth_src_addr[ETH_ALEN];
+	unsigned char	dsa_tag[DSA_LEN];
+	__be16		eth_ethertype;
+} __attribute__((packed));
+
+struct mv88e6xxx_rmu_request {
+	__be16	format;
+	__be16	pad; /* 0x0000 on request, Prod Num/Rev on response */
+	__be16	code;
+} __attribute__((packed));
+
+static int mv88e6xxx_rmu_request(struct mv88e6xxx_chip *chip, u16 code, u8 *data, size_t len)
+{
+	const unsigned char dest_addr[ETH_ALEN] = { 0x01, 0x50, 0x43, 0x00, 0x00, 0x00 };
+	struct net_device *dev = chip->rmu_dev;
+	struct mv88e6xxx_rmu_request *req;
+	unsigned char *eth_dest_addr;
+	unsigned char *eth_src_addr;
+	unsigned char *dsa_tag;
+	__be16 *eth_ethertype;
+	struct edsahdr *edsa;
+	struct sk_buff *skb;
+	struct dsahdr *dsa;
+
+	if (!dev)
+		return -EOPNOTSUPP;
+
+	switch (dev->dsa_ptr->tag_ops->proto) {
+	case DSA_TAG_PROTO_DSA:
+		skb = alloc_skb(sizeof(*dsa) + sizeof(*req) + len, GFP_KERNEL);
+		if (!skb)
+			return -ENOMEM;
+
+		dsa = skb_put(skb, sizeof(*dsa));
+		eth_dest_addr = dsa->eth_dest_addr;
+		eth_src_addr = dsa->eth_src_addr;
+		dsa_tag = dsa->dsa_tag;
+		eth_ethertype = &dsa->eth_ethertype;
+		break;
+	case DSA_TAG_PROTO_EDSA:
+		skb = alloc_skb(sizeof(*edsa) + sizeof(*req) + len, GFP_KERNEL);
+		if (!skb)
+			return -ENOMEM;
+
+		edsa = skb_put(skb, sizeof(*edsa));
+		eth_dest_addr = edsa->eth_dest_addr;
+		eth_src_addr = edsa->eth_src_addr;
+		edsa->edsa_ethertype = htons(ETH_P_EDSA);
+		edsa->edsa_reserved = 0x0000;
+		dsa_tag = edsa->dsa_tag;
+		eth_ethertype = &edsa->eth_ethertype;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	ether_addr_copy(eth_dest_addr, dest_addr); /* Marvell broadcast or switch MAC */
+	ether_addr_copy(eth_src_addr, dev->dev_addr);
+	dsa_tag[0] = 0x40 | (chip->ds->index & 0x1f); /* From_CPU */
+	dsa_tag[1] = 0xfa;
+	dsa_tag[2] = 0xf;
+	dsa_tag[3] = ++chip->rmu_sequence_num;
+	*eth_ethertype = htons(ETH_P_EDSA); /* User defined, useless really */
+
+	req = skb_put(skb, sizeof(*req));
+	req->format = htons(MV88E6XXX_RMU_REQUEST_FORMAT_SOHO);
+	req->pad = 0x0000;
+	req->code = htons(code);
+
+	skb_put_data(skb, data, len);
+
+	skb->dev = dev;
+
+	dsa_switch_xmit(chip->ds, skb);
+
+	return mv88e6xxx_rmu_wait_response(chip);
+}
+
+int mv88e6xxx_rmu_response(struct mv88e6xxx_chip *chip, struct sk_buff *skb)
+{
+	struct net_device *dev = chip->rmu_dev;
+	struct mv88e6xxx_rmu_request *req;
+	unsigned char *dsa_tag;
+	size_t data_offset; 
+	size_t req_offset;
+
+	/* Check if RMU is enabled */
+	if (dev != skb->dev)
+		return -EOPNOTSUPP;
+
+	if (chip->rmu_response)
+		return -EBUSY;
+
+	switch (dev->dsa_ptr->tag_ops->proto) {
+	case DSA_TAG_PROTO_DSA:
+		dsa_tag = skb->data - 2;
+		req_offset = DSA_LEN + ETH_TLEN - 2;
+		break;
+	case DSA_TAG_PROTO_EDSA:
+		/* skb->data points to the end of the (EDSA) ethertype */
+		dsa_tag = skb->data + 2;
+		req_offset = 2 + DSA_LEN + ETH_TLEN;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if ((dsa_tag[0] != chip->ds->index) ||
+            (dsa_tag[1] != 0x00) ||
+            ((dsa_tag[2] & 0x1f) != 0x1f) ||
+            (dsa_tag[3] != chip->rmu_sequence_num))
+		return -EINVAL;
+
+	data_offset = req_offset + sizeof(*req);
+	if (skb->len < data_offset)
+		return -EINVAL;
+
+	req = (struct mv88e6xxx_rmu_request *)(skb->data + req_offset);
+	if (ntohs(req->code) == 0xffff)
+		return -EINVAL;
+
+	chip->rmu_response_data_len = skb->len - data_offset;
+	if (chip->rmu_response_data_len > 0)
+		chip->rmu_response_data = skb->data + data_offset;
+	else
+		chip->rmu_response_data = NULL;
+
+	chip->rmu_response = skb_clone(skb, GFP_KERNEL);
+	if (!chip->rmu_response)
+		return -ENOMEM;
+
+	complete(&chip->rmu_response_received);
+
+	return 0;
+}
+
+static int mv88e6xxx_rmu_reg_read(struct mv88e6xxx_chip *chip,
+				  int dev, int reg, u16 *data)
+{
+	unsigned char request_data[8];
+	int err;
+
+	request_data[0] = 0x08 | ((dev >> 3) & 0x03);
+	request_data[1] = ((dev << 5) & 0xe0) | (reg & 0x1f);
+	request_data[2] = 0x00;
+	request_data[3] = 0x00;
+
+	/* End Of List Command */
+	memset(&request_data[4], 0xff, 4);
+
+	err = mv88e6xxx_rmu_request(chip, MV88E6XXX_RMU_REQUEST_CODE_READ_WRITE, request_data, sizeof(request_data));
+	if (err)
+		return err;
+
+	if (chip->rmu_response_data_len < sizeof(request_data))
+		err = -EINVAL;
+	else
+		*data = (chip->rmu_response_data[2] << 8) | chip->rmu_response_data[3];
+
+	kfree_skb(chip->rmu_response);
+	chip->rmu_response = NULL;
+
+	return err;
+}
+
+static int mv88e6xxx_rmu_reg_write(struct mv88e6xxx_chip *chip,
+				   int dev, int reg, u16 data)
+{
+	unsigned char request_data[8];
+	int err;
+
+	request_data[0] = 0x04 | ((dev >> 3) & 0x03);
+	request_data[1] = ((dev << 5) & 0xe0) | (reg & 0x1f);
+	request_data[2] = data >> 8;
+	request_data[3] = data & 0xff;
+
+	/* End Of List Command */
+	memset(&request_data[4], 0xff, 4);
+
+	err = mv88e6xxx_rmu_request(chip, MV88E6XXX_RMU_REQUEST_CODE_READ_WRITE, request_data, sizeof(request_data));
+	if (err)
+		return err;
+
+	if (chip->rmu_response_data_len < sizeof(request_data))
+		err = -EINVAL;
+
+	kfree_skb(chip->rmu_response);
+	chip->rmu_response = NULL;
+
+	return err;
+}
+
+static const struct mv88e6xxx_bus_ops mv88e6xxx_rmu_ops = {
+	.read = mv88e6xxx_rmu_reg_read,
+	.write = mv88e6xxx_rmu_reg_write,
+};
+
+static int mv88e6xxx_rmu_setup_bus(struct mv88e6xxx_chip *chip,
+				      struct net_device *dev)
+{
+	chip->rmu_ops = &mv88e6xxx_rmu_ops;
+	chip->rmu_dev = dev;
+
+	init_completion(&chip->rmu_response_received);
+
+	dev_info(chip->dev, "RMU reachable via %s\n", netdev_name(dev));
+
+	if (!chip->ops)
+		chip->ops = chip->rmu_ops;
+
+	return 0;
+}
+
 static int mv88e6xxx_rmu_setup_port(struct mv88e6xxx_chip *chip, int port)
 {
 	int err;
@@ -40,6 +286,7 @@ static int mv88e6xxx_rmu_setup_port(struct mv88e6xxx_chip *chip, int port)
 int mv88e6xxx_rmu_setup(struct mv88e6xxx_chip *chip)
 {
 	struct dsa_switch *ds = chip->ds;
+	struct net_device *dev;
 	int port;
 	int err;
 
@@ -50,6 +297,15 @@ int mv88e6xxx_rmu_setup(struct mv88e6xxx_chip *chip)
 			if (err)
 				continue;
 
+			/* When the control CPU is local, use the master interface */
+			dev = dsa_to_master(ds, port);
+			if (!dev)
+				return -ENODEV;
+
+			err = mv88e6xxx_rmu_setup_bus(chip, dev);
+			if (err)
+				return err;
+
 			return 0;
 		}
 	}
diff --git a/drivers/net/dsa/mv88e6xxx/rmu.h b/drivers/net/dsa/mv88e6xxx/rmu.h
index f7d849b169d2..099df0789bb5 100644
--- a/drivers/net/dsa/mv88e6xxx/rmu.h
+++ b/drivers/net/dsa/mv88e6xxx/rmu.h
@@ -29,5 +29,6 @@
 #include "chip.h"
 
 int mv88e6xxx_rmu_setup(struct mv88e6xxx_chip *chip);
+int mv88e6xxx_rmu_response(struct mv88e6xxx_chip *chip, struct sk_buff *skb);
 
 #endif /* _MV88E6XXX_RMU_H */
-- 
2.21.0


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* Re: [RFC net-next 9/9] net: dsa: mv88e6xxx: setup RMU bus
  2019-05-21 19:30 ` [RFC net-next 9/9] net: dsa: mv88e6xxx: setup RMU bus Vivien Didelot
@ 2019-05-21 20:42   ` Andrew Lunn
  0 siblings, 0 replies; 11+ messages in thread
From: Andrew Lunn @ 2019-05-21 20:42 UTC (permalink / raw)
  To: Vivien Didelot; +Cc: netdev, cphealy, Florian Fainelli, David S. Miller

> +	ether_addr_copy(eth_dest_addr, dest_addr); /* Marvell broadcast or switch MAC */
> +	ether_addr_copy(eth_src_addr, dev->dev_addr);
> +	dsa_tag[0] = 0x40 | (chip->ds->index & 0x1f); /* From_CPU */
> +	dsa_tag[1] = 0xfa;
> +	dsa_tag[2] = 0xf;
> +	dsa_tag[3] = ++chip->rmu_sequence_num;
> +	*eth_ethertype = htons(ETH_P_EDSA); /* User defined, useless really */
> +
> +	req = skb_put(skb, sizeof(*req));
> +	req->format = htons(MV88E6XXX_RMU_REQUEST_FORMAT_SOHO);
> +	req->pad = 0x0000;
> +	req->code = htons(code);

Hi Vivien

When i looked at this before, i had a different idea how to do this.

The EthType you put into this header is also used in the reply. So we
could define an ETH_P_RMU. It is then possible to get the stack to
pass all frames for an ether type to a handler. e.g the batman code:

int batadv_hardif_enable_interface(struct batadv_hard_iface *hard_iface,
                                   struct net *net, const char *iface_name)
{
        struct batadv_priv *bat_priv;
        struct net_device *soft_iface, *master;
        __be16 ethertype = htons(ETH_P_BATMAN);

        hard_iface->batman_adv_ptype.type = ethertype;
        hard_iface->batman_adv_ptype.func = batadv_batman_skb_recv;
        hard_iface->batman_adv_ptype.dev = hard_iface->net_dev;
        dev_add_pack(&hard_iface->batman_adv_ptype);

So i would let the tag driver take off the {E}DSA header and pass the
frame to the stack, and the stack can then identify the frame and pass
it to the switch driver. I think it makes the split between tag code
and switch code cleaner.

       Andrew

^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2019-05-21 20:42 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-05-21 19:29 [RFC net-next 0/9] net: dsa: mv88e6xxx: add RMU support Vivien Didelot
2019-05-21 19:29 ` [RFC net-next 1/9] net: dsa: introduce dsa_master_find_switch Vivien Didelot
2019-05-21 19:29 ` [RFC net-next 2/9] net: dsa: allow switches to receive frames Vivien Didelot
2019-05-21 19:29 ` [RFC net-next 3/9] net: dsa: allow switches to transmit frames Vivien Didelot
2019-05-21 19:29 ` [RFC net-next 4/9] net: dsa: introduce dsa_is_upstream_port Vivien Didelot
2019-05-21 19:30 ` [RFC net-next 5/9] net: dsa: introduce dsa_to_master Vivien Didelot
2019-05-21 19:30 ` [RFC net-next 6/9] net: dsa: mv88e6xxx: add default bus operations Vivien Didelot
2019-05-21 19:30 ` [RFC net-next 7/9] net: dsa: mv88e6xxx: implement RMU enable Vivien Didelot
2019-05-21 19:30 ` [RFC net-next 8/9] net: dsa: mv88e6xxx: setup RMU port Vivien Didelot
2019-05-21 19:30 ` [RFC net-next 9/9] net: dsa: mv88e6xxx: setup RMU bus Vivien Didelot
2019-05-21 20:42   ` Andrew Lunn

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).