Commit abd5576b authored by MD Danish Anwar's avatar MD Danish Anwar Committed by Paolo Abeni
Browse files

net: ti: icssg-prueth: Add support for ICSSG switch firmware



Add support for ICSSG switch firmware using existing Dual EMAC driver
with switchdev.

Limitations:
VLAN offloading is limited to 0-256 IDs.
MDB/FDB static entries are limited to 511 entries and different FDBs can
hash to same bucket and thus may not completely offloaded

Example assuming ETH1 and ETH2 as ICSSG2 interfaces:

Switch to ICSSG Switch mode:
 ip link add name br0 type bridge
 ip link set dev eth1 master br0
 ip link set dev eth2 master br0
 ip link set dev br0 up
 bridge vlan add dev br0 vid 1 pvid untagged self

Going back to Dual EMAC mode:

 ip link set dev br0 down
 ip link set dev eth1 nomaster
 ip link set dev eth2 nomaster
 ip link del name br0 type bridge

By default, Dual EMAC firmware is loaded, and can be changed to switch
mode by above steps

Reviewed-by: default avatarAndrew Lunn <andrew@lunn.ch>
Signed-off-by: default avatarMD Danish Anwar <danishanwar@ti.com>
Signed-off-by: default avatarPaolo Abeni <pabeni@redhat.com>
parent 972383ae
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -204,6 +204,7 @@ config TI_ICSSG_PRUETH_SR1
	select TI_ICSS_IEP
	select TI_K3_CPPI_DESC_POOL
	depends on PRU_REMOTEPROC
	depends on NET_SWITCHDEV
	depends on ARCH_K3 && OF && TI_K3_UDMA_GLUE_LAYER
	help
	  Support dual Gigabit Ethernet ports over the ICSSG PRU Subsystem.
+2 −1
Original line number Diff line number Diff line
@@ -39,7 +39,8 @@ icssg-prueth-y := icssg/icssg_prueth.o \
		  icssg/icssg_config.o \
		  icssg/icssg_mii_cfg.o \
		  icssg/icssg_stats.o \
		  icssg/icssg_ethtool.o
		  icssg/icssg_ethtool.o \
		  icssg/icssg_switchdev.o
obj-$(CONFIG_TI_ICSSG_PRUETH_SR1) += icssg-prueth-sr1.o
icssg-prueth-sr1-y := icssg/icssg_prueth_sr1.o \
		      icssg/icssg_common.o \
+1 −1
Original line number Diff line number Diff line
@@ -455,7 +455,7 @@ void icssg_ft1_set_mac_addr(struct regmap *miig_rt, int slice, u8 *mac_addr)
{
	const u8 mask_addr[] = { 0, 0, 0, 0, 0, 0, };

	rx_class_ft1_set_start_len(miig_rt, slice, 0, 6);
	rx_class_ft1_set_start_len(miig_rt, slice, 6, 6);
	rx_class_ft1_set_da(miig_rt, slice, 0, mac_addr);
	rx_class_ft1_set_da_mask(miig_rt, slice, 0, mask_addr);
	rx_class_ft1_cfg_set_type(miig_rt, slice, 0, FT1_CFG_TYPE_EQ);
+2 −0
Original line number Diff line number Diff line
@@ -581,6 +581,8 @@ static int emac_rx_packet(struct prueth_emac *emac, u32 flow_id)
	} else {
		/* send the filled skb up the n/w stack */
		skb_put(skb, pkt_len);
		if (emac->prueth->is_switch_mode)
			skb->offload_fwd_mark = emac->offload_fwd_mark;
		skb->protocol = eth_type_trans(skb, ndev);
		napi_gro_receive(&emac->napi_rx, skb);
		ndev->stats.rx_bytes += pkt_len;
+138 −14
Original line number Diff line number Diff line
@@ -107,28 +107,49 @@ static const struct map hwq_map[2][ICSSG_NUM_OTHER_QUEUES] = {
	},
};

static void icssg_config_mii_init_switch(struct prueth_emac *emac)
{
	struct prueth *prueth = emac->prueth;
	int mii = prueth_emac_slice(emac);
	u32 txcfg_reg, pcnt_reg, txcfg;
	struct regmap *mii_rt;

	mii_rt = prueth->mii_rt;

	txcfg_reg = (mii == ICSS_MII0) ? PRUSS_MII_RT_TXCFG0 :
				       PRUSS_MII_RT_TXCFG1;
	pcnt_reg = (mii == ICSS_MII0) ? PRUSS_MII_RT_RX_PCNT0 :
				       PRUSS_MII_RT_RX_PCNT1;

	txcfg = PRUSS_MII_RT_TXCFG_TX_ENABLE |
		PRUSS_MII_RT_TXCFG_TX_AUTO_PREAMBLE |
		PRUSS_MII_RT_TXCFG_TX_IPG_WIRE_CLK_EN;

	if (emac->phy_if == PHY_INTERFACE_MODE_MII && mii == ICSS_MII1)
		txcfg |= PRUSS_MII_RT_TXCFG_TX_MUX_SEL;
	else if (emac->phy_if != PHY_INTERFACE_MODE_MII && mii == ICSS_MII0)
		txcfg |= PRUSS_MII_RT_TXCFG_TX_MUX_SEL;

	regmap_write(mii_rt, txcfg_reg, txcfg);
	regmap_write(mii_rt, pcnt_reg, 0x1);
}

static void icssg_config_mii_init(struct prueth_emac *emac)
{
	u32 rxcfg, txcfg, rxcfg_reg, txcfg_reg, pcnt_reg;
	struct prueth *prueth = emac->prueth;
	int slice = prueth_emac_slice(emac);
	u32 txcfg, txcfg_reg, pcnt_reg;
	struct regmap *mii_rt;

	mii_rt = prueth->mii_rt;

	rxcfg_reg = (slice == ICSS_MII0) ? PRUSS_MII_RT_RXCFG0 :
				       PRUSS_MII_RT_RXCFG1;
	txcfg_reg = (slice == ICSS_MII0) ? PRUSS_MII_RT_TXCFG0 :
				       PRUSS_MII_RT_TXCFG1;
	pcnt_reg = (slice == ICSS_MII0) ? PRUSS_MII_RT_RX_PCNT0 :
				       PRUSS_MII_RT_RX_PCNT1;

	rxcfg = MII_RXCFG_DEFAULT;
	txcfg = MII_TXCFG_DEFAULT;

	if (slice == ICSS_MII1)
		rxcfg |= PRUSS_MII_RT_RXCFG_RX_MUX_SEL;

	/* In MII mode TX lines swapped inside ICSSG, so TX_MUX_SEL cfg need
	 * to be swapped also comparing to RGMII mode.
	 */
@@ -137,7 +158,6 @@ static void icssg_config_mii_init(struct prueth_emac *emac)
	else if (emac->phy_if != PHY_INTERFACE_MODE_MII && slice == ICSS_MII1)
		txcfg |= PRUSS_MII_RT_TXCFG_TX_MUX_SEL;

	regmap_write(mii_rt, rxcfg_reg, rxcfg);
	regmap_write(mii_rt, txcfg_reg, txcfg);
	regmap_write(mii_rt, pcnt_reg, 0x1);
}
@@ -257,6 +277,66 @@ static int emac_r30_is_done(struct prueth_emac *emac)
	return 1;
}

static int prueth_switch_buffer_setup(struct prueth_emac *emac)
{
	struct icssg_buffer_pool_cfg __iomem *bpool_cfg;
	struct icssg_rxq_ctx __iomem *rxq_ctx;
	struct prueth *prueth = emac->prueth;
	int slice = prueth_emac_slice(emac);
	u32 addr;
	int i;

	addr = lower_32_bits(prueth->msmcram.pa);
	if (slice)
		addr += PRUETH_NUM_BUF_POOLS * PRUETH_EMAC_BUF_POOL_SIZE;

	if (addr % SZ_64K) {
		dev_warn(prueth->dev, "buffer pool needs to be 64KB aligned\n");
		return -EINVAL;
	}

	bpool_cfg = emac->dram.va + BUFFER_POOL_0_ADDR_OFFSET;
	/* workaround for f/w bug. bpool 0 needs to be initialized */
	for (i = 0; i <  PRUETH_NUM_BUF_POOLS; i++) {
		writel(addr, &bpool_cfg[i].addr);
		writel(PRUETH_EMAC_BUF_POOL_SIZE, &bpool_cfg[i].len);
		addr += PRUETH_EMAC_BUF_POOL_SIZE;
	}

	if (!slice)
		addr += PRUETH_NUM_BUF_POOLS * PRUETH_EMAC_BUF_POOL_SIZE;
	else
		addr += PRUETH_SW_NUM_BUF_POOLS_HOST * PRUETH_SW_BUF_POOL_SIZE_HOST;

	for (i = PRUETH_NUM_BUF_POOLS;
	     i < 2 * PRUETH_SW_NUM_BUF_POOLS_HOST + PRUETH_NUM_BUF_POOLS;
	     i++) {
		/* The driver only uses first 4 queues per PRU so only initialize them */
		if (i % PRUETH_SW_NUM_BUF_POOLS_HOST < PRUETH_SW_NUM_BUF_POOLS_PER_PRU) {
			writel(addr, &bpool_cfg[i].addr);
			writel(PRUETH_SW_BUF_POOL_SIZE_HOST, &bpool_cfg[i].len);
			addr += PRUETH_SW_BUF_POOL_SIZE_HOST;
		} else {
			writel(0, &bpool_cfg[i].addr);
			writel(0, &bpool_cfg[i].len);
		}
	}

	if (!slice)
		addr += PRUETH_SW_NUM_BUF_POOLS_HOST * PRUETH_SW_BUF_POOL_SIZE_HOST;
	else
		addr += PRUETH_EMAC_RX_CTX_BUF_SIZE;

	rxq_ctx = emac->dram.va + HOST_RX_Q_PRE_CONTEXT_OFFSET;
	for (i = 0; i < 3; i++)
		writel(addr, &rxq_ctx->start[i]);

	addr += PRUETH_EMAC_RX_CTX_BUF_SIZE;
	writel(addr - SZ_2K, &rxq_ctx->end);

	return 0;
}

static int prueth_emac_buffer_setup(struct prueth_emac *emac)
{
	struct icssg_buffer_pool_cfg __iomem *bpool_cfg;
@@ -321,24 +401,62 @@ static void icssg_init_emac_mode(struct prueth *prueth)
	/* When the device is configured as a bridge and it is being brought
	 * back to the emac mode, the host mac address has to be set as 0.
	 */
	u32 addr = prueth->shram.pa + EMAC_ICSSG_SWITCH_DEFAULT_VLAN_TABLE_OFFSET;
	int i;
	u8 mac[ETH_ALEN] = { 0 };

	if (prueth->emacs_initialized)
		return;

	regmap_update_bits(prueth->miig_rt, FDB_GEN_CFG1,
			   SMEM_VLAN_OFFSET_MASK, 0);
	regmap_write(prueth->miig_rt, FDB_GEN_CFG2, 0);
	/* Set VLAN TABLE address base */
	regmap_update_bits(prueth->miig_rt, FDB_GEN_CFG1, SMEM_VLAN_OFFSET_MASK,
			   addr <<  SMEM_VLAN_OFFSET);
	/* Set enable VLAN aware mode, and FDBs for all PRUs */
	regmap_write(prueth->miig_rt, FDB_GEN_CFG2, (FDB_PRU0_EN | FDB_PRU1_EN | FDB_HOST_EN));
	prueth->vlan_tbl = (struct prueth_vlan_tbl __force *)(prueth->shram.va +
			    EMAC_ICSSG_SWITCH_DEFAULT_VLAN_TABLE_OFFSET);
	for (i = 0; i < SZ_4K - 1; i++) {
		prueth->vlan_tbl[i].fid = i;
		prueth->vlan_tbl[i].fid_c1 = 0;
	}
	/* Clear host MAC address */
	icssg_class_set_host_mac_addr(prueth->miig_rt, mac);
}

static void icssg_init_switch_mode(struct prueth *prueth)
{
	u32 addr = prueth->shram.pa + EMAC_ICSSG_SWITCH_DEFAULT_VLAN_TABLE_OFFSET;
	int i;

	if (prueth->emacs_initialized)
		return;

	/* Set VLAN TABLE address base */
	regmap_update_bits(prueth->miig_rt, FDB_GEN_CFG1, SMEM_VLAN_OFFSET_MASK,
			   addr <<  SMEM_VLAN_OFFSET);
	/* Set enable VLAN aware mode, and FDBs for all PRUs */
	regmap_write(prueth->miig_rt, FDB_GEN_CFG2, FDB_EN_ALL);
	prueth->vlan_tbl = (struct prueth_vlan_tbl __force *)(prueth->shram.va +
			    EMAC_ICSSG_SWITCH_DEFAULT_VLAN_TABLE_OFFSET);
	for (i = 0; i < SZ_4K - 1; i++) {
		prueth->vlan_tbl[i].fid = i;
		prueth->vlan_tbl[i].fid_c1 = 0;
	}

	if (prueth->hw_bridge_dev)
		icssg_class_set_host_mac_addr(prueth->miig_rt, prueth->hw_bridge_dev->dev_addr);
	icssg_set_pvid(prueth, prueth->default_vlan, PRUETH_PORT_HOST);
}

int icssg_config(struct prueth *prueth, struct prueth_emac *emac, int slice)
{
	void __iomem *config = emac->dram.va + ICSSG_CONFIG_OFFSET;
	struct icssg_flow_cfg __iomem *flow_cfg;
	int ret;

	if (prueth->is_switch_mode)
		icssg_init_switch_mode(prueth);
	else
		icssg_init_emac_mode(prueth);

	memset_io(config, 0, TAS_GATE_MASK_LIST0);
@@ -353,6 +471,9 @@ int icssg_config(struct prueth *prueth, struct prueth_emac *emac, int slice)
	regmap_update_bits(prueth->miig_rt, ICSSG_CFG_OFFSET,
			   ICSSG_CFG_DEFAULT, ICSSG_CFG_DEFAULT);
	icssg_miig_set_interface_mode(prueth->miig_rt, slice, emac->phy_if);
	if (prueth->is_switch_mode)
		icssg_config_mii_init_switch(emac);
	else
		icssg_config_mii_init(emac);
	icssg_config_ipg(emac);
	icssg_update_rgmii_cfg(prueth->miig_rt, emac);
@@ -376,6 +497,9 @@ int icssg_config(struct prueth *prueth, struct prueth_emac *emac, int slice)
	writeb(0, config + SPL_PKT_DEFAULT_PRIORITY);
	writeb(0, config + QUEUE_NUM_UNTAGGED);

	if (prueth->is_switch_mode)
		ret = prueth_switch_buffer_setup(emac);
	else
		ret = prueth_emac_buffer_setup(emac);
	if (ret)
		return ret;
Loading