Commit 6c601aac authored by David S. Miller's avatar David S. Miller
Browse files

Merge branch 'RTL8366RB-enhancements'



Linus Walleij says:

====================
RTL8366RB enhancements

This patch set is a set of reasonably mature improvements
for the RTL8366RB switch, implemented after Vladimir
challenged me to dig deeper into the switch functions.

ChangeLog v4->v5:
- Drop dubious flood control patch: these registers probably
  only deal with rate limiting, we will deal with this
  another time if we can figure it out.

ChangeLog -> v4:
- Rebase earlier circulated patches on the now merged
  VLAN set-up cleanups.
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents bcb2293d e674cfd0
Loading
Loading
Loading
Loading
+106 −6
Original line number Diff line number Diff line
@@ -14,6 +14,7 @@

#include <linux/bitops.h>
#include <linux/etherdevice.h>
#include <linux/if_bridge.h>
#include <linux/interrupt.h>
#include <linux/irqdomain.h>
#include <linux/irqchip/chained_irq.h>
@@ -42,9 +43,12 @@
/* Port Enable Control register */
#define RTL8366RB_PECR				0x0001

/* Switch Security Control registers */
#define RTL8366RB_SSCR0				0x0002
#define RTL8366RB_SSCR1				0x0003
/* Switch per-port learning disablement register */
#define RTL8366RB_PORT_LEARNDIS_CTRL		0x0002

/* Security control, actually aging register */
#define RTL8366RB_SECURITY_CTRL			0x0003

#define RTL8366RB_SSCR2				0x0004
#define RTL8366RB_SSCR2_DROP_UNKNOWN_DA		BIT(0)

@@ -106,6 +110,18 @@

#define RTL8366RB_POWER_SAVING_REG	0x0021

/* Spanning tree status (STP) control, two bits per port per FID */
#define RTL8366RB_STP_STATE_BASE	0x0050 /* 0x0050..0x0057 */
#define RTL8366RB_STP_STATE_DISABLED	0x0
#define RTL8366RB_STP_STATE_BLOCKING	0x1
#define RTL8366RB_STP_STATE_LEARNING	0x2
#define RTL8366RB_STP_STATE_FORWARDING	0x3
#define RTL8366RB_STP_MASK		GENMASK(1, 0)
#define RTL8366RB_STP_STATE(port, state) \
	((state) << ((port) * 2))
#define RTL8366RB_STP_STATE_MASK(port) \
	RTL8366RB_STP_STATE((port), RTL8366RB_STP_MASK)

/* CPU port control reg */
#define RTL8368RB_CPU_CTRL_REG		0x0061
#define RTL8368RB_CPU_PORTS_MSK		0x00FF
@@ -230,6 +246,7 @@
#define RTL8366RB_NUM_LEDGROUPS		4
#define RTL8366RB_NUM_VIDS		4096
#define RTL8366RB_PRIORITYMAX		7
#define RTL8366RB_NUM_FIDS		8
#define RTL8366RB_FIDMAX		7

#define RTL8366RB_PORT_1		BIT(0) /* In userspace port 0 */
@@ -927,13 +944,14 @@ static int rtl8366rb_setup(struct dsa_switch *ds)
		/* layer 2 size, see rtl8366rb_change_mtu() */
		rb->max_mtu[i] = 1532;

	/* Enable learning for all ports */
	ret = regmap_write(smi->map, RTL8366RB_SSCR0, 0);
	/* Disable learning for all ports */
	ret = regmap_write(smi->map, RTL8366RB_PORT_LEARNDIS_CTRL,
			   RTL8366RB_PORT_ALL);
	if (ret)
		return ret;

	/* Enable auto ageing for all ports */
	ret = regmap_write(smi->map, RTL8366RB_SSCR1, 0);
	ret = regmap_write(smi->map, RTL8366RB_SECURITY_CTRL, 0);
	if (ret)
		return ret;

@@ -1272,6 +1290,84 @@ static int rtl8366rb_vlan_filtering(struct dsa_switch *ds, int port,
	return ret;
}

static int
rtl8366rb_port_pre_bridge_flags(struct dsa_switch *ds, int port,
				struct switchdev_brport_flags flags,
				struct netlink_ext_ack *extack)
{
	/* We support enabling/disabling learning */
	if (flags.mask & ~(BR_LEARNING))
		return -EINVAL;

	return 0;
}

static int
rtl8366rb_port_bridge_flags(struct dsa_switch *ds, int port,
			    struct switchdev_brport_flags flags,
			    struct netlink_ext_ack *extack)
{
	struct realtek_smi *smi = ds->priv;
	int ret;

	if (flags.mask & BR_LEARNING) {
		ret = regmap_update_bits(smi->map, RTL8366RB_PORT_LEARNDIS_CTRL,
					 BIT(port),
					 (flags.val & BR_LEARNING) ? 0 : BIT(port));
		if (ret)
			return ret;
	}

	return 0;
}

static void
rtl8366rb_port_stp_state_set(struct dsa_switch *ds, int port, u8 state)
{
	struct realtek_smi *smi = ds->priv;
	u32 val;
	int i;

	switch (state) {
	case BR_STATE_DISABLED:
		val = RTL8366RB_STP_STATE_DISABLED;
		break;
	case BR_STATE_BLOCKING:
	case BR_STATE_LISTENING:
		val = RTL8366RB_STP_STATE_BLOCKING;
		break;
	case BR_STATE_LEARNING:
		val = RTL8366RB_STP_STATE_LEARNING;
		break;
	case BR_STATE_FORWARDING:
		val = RTL8366RB_STP_STATE_FORWARDING;
		break;
	default:
		dev_err(smi->dev, "unknown bridge state requested\n");
		return;
	};

	/* Set the same status for the port on all the FIDs */
	for (i = 0; i < RTL8366RB_NUM_FIDS; i++) {
		regmap_update_bits(smi->map, RTL8366RB_STP_STATE_BASE + i,
				   RTL8366RB_STP_STATE_MASK(port),
				   RTL8366RB_STP_STATE(port, val));
	}
}

static void
rtl8366rb_port_fast_age(struct dsa_switch *ds, int port)
{
	struct realtek_smi *smi = ds->priv;

	/* This will age out any learned L2 entries */
	regmap_update_bits(smi->map, RTL8366RB_SECURITY_CTRL,
			   BIT(port), BIT(port));
	/* Restore the normal state of things */
	regmap_update_bits(smi->map, RTL8366RB_SECURITY_CTRL,
			   BIT(port), 0);
}

static int rtl8366rb_change_mtu(struct dsa_switch *ds, int port, int new_mtu)
{
	struct realtek_smi *smi = ds->priv;
@@ -1682,6 +1778,10 @@ static const struct dsa_switch_ops rtl8366rb_switch_ops = {
	.port_vlan_del = rtl8366_vlan_del,
	.port_enable = rtl8366rb_port_enable,
	.port_disable = rtl8366rb_port_disable,
	.port_pre_bridge_flags = rtl8366rb_port_pre_bridge_flags,
	.port_bridge_flags = rtl8366rb_port_bridge_flags,
	.port_stp_state_set = rtl8366rb_port_stp_state_set,
	.port_fast_age = rtl8366rb_port_fast_age,
	.port_change_mtu = rtl8366rb_change_mtu,
	.port_max_mtu = rtl8366rb_max_mtu,
};