From 660db35a75aa03535153dddda527dc9a62f090e9 Mon Sep 17 00:00:00 2001 From: Michael Grzeschik Date: Mon, 4 May 2020 09:50:18 +0200 Subject: net: cpsw: fix cpsw_slave_regs register offsets The cpsw_slave_regs struct also start with max_blks register, so here we also have to skip the first two bytes like for .host_port_reg_ofs Signed-off-by: Michael Grzeschik Signed-off-by: Sascha Hauer --- drivers/net/cpsw.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/cpsw.c b/drivers/net/cpsw.c index b9a6575009..7fba4d27f9 100644 --- a/drivers/net/cpsw.c +++ b/drivers/net/cpsw.c @@ -1015,7 +1015,7 @@ static struct cpsw_data cpsw1_data = { .cpdma_reg_ofs = 0x100, .state_ram_ofs = 0x200, .ale_reg_ofs = 0x600, - .slave_ofs = 0x050, + .slave_ofs = 0x058, .slave_size = 0x040, .sliver_ofs = 0x700, /* FIXME: mdio_reg_ofs and cppi_ram_ofs missing */ @@ -1026,7 +1026,7 @@ static struct cpsw_data cpsw2_data = { .cpdma_reg_ofs = 0x800, .state_ram_ofs = 0xa00, .ale_reg_ofs = 0xd00, - .slave_ofs = 0x200, + .slave_ofs = 0x208, .slave_size = 0x100, .sliver_ofs = 0xd80, .mdio_reg_ofs = 0x1000, -- cgit v1.2.3 From 89a6e4fa3e4c081f2140b23cfc562b3898c09ded Mon Sep 17 00:00:00 2001 From: Michael Grzeschik Date: Mon, 4 May 2020 09:50:19 +0200 Subject: net: cpsw: move generic setup code to probe All generic cpsw code can move to probe, so the controller is only reset and configured once. Signed-off-by: Michael Grzeschik Signed-off-by: Sascha Hauer --- drivers/net/cpsw.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/net/cpsw.c b/drivers/net/cpsw.c index 7fba4d27f9..93df5aed5c 100644 --- a/drivers/net/cpsw.c +++ b/drivers/net/cpsw.c @@ -801,7 +801,7 @@ static int cpsw_open(struct eth_device *edev) { struct cpsw_slave *slave = edev->priv; struct cpsw_priv *priv = slave->cpsw; - int i, ret; + int ret; dev_dbg(&slave->dev, "* %s\n", __func__); @@ -810,6 +810,16 @@ static int cpsw_open(struct eth_device *edev) if (ret) return ret; + cpsw_slave_init(slave, priv); + + return 0; +} + +static int cpsw_setup(struct device_d *dev) +{ + struct cpsw_priv *priv = dev->priv; + int i, ret; + /* soft reset the controller and initialize priv */ soft_reset(priv, &priv->regs->soft_reset); @@ -834,9 +844,6 @@ static int cpsw_open(struct eth_device *edev) cpsw_ale_add_ucast(priv, priv->mac_addr, priv->host_port, ALE_SECURE); cpsw_ale_add_mcast(priv, ethbdaddr, 1 << priv->host_port); - - cpsw_slave_init(slave, priv); - /* init descriptor pool */ for (i = 0; i < NUM_DESCS; i++) { u32 val = (i == (NUM_DESCS - 1)) ? 0 : (u32)&priv->descs[i + 1]; @@ -875,7 +882,7 @@ static int cpsw_open(struct eth_device *edev) ret = cpdma_submit(priv, &priv->rx_chan, NetRxPackets[i], PKTSIZE); if (ret < 0) { - dev_err(&slave->dev, "error %d submitting rx desc\n", ret); + dev_err(dev, "error %d submitting rx desc\n", ret); break; } } @@ -1241,6 +1248,8 @@ static int cpsw_probe(struct device_d *dev) dev->priv = priv; + cpsw_setup(dev); + return 0; out: free(priv->slaves); -- cgit v1.2.3 From d51e45904814b9f061ba1e301bd080158735a233 Mon Sep 17 00:00:00 2001 From: Michael Grzeschik Date: Mon, 4 May 2020 09:50:20 +0200 Subject: net: cpsw: cpsw_process should only handle rx channels for its own port The driver is currently processing the whole channel list, so it is possible that it handles a channel of the wrong interface. We limit the rx_chan handling for its desired port. The cpsw_send is removing finished tx_chan transfers on each send, so this has no directional limitation. Signed-off-by: Michael Grzeschik Signed-off-by: Sascha Hauer --- drivers/net/cpsw.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/cpsw.c b/drivers/net/cpsw.c index 93df5aed5c..ced77dd401 100644 --- a/drivers/net/cpsw.c +++ b/drivers/net/cpsw.c @@ -52,6 +52,9 @@ #define CPDMA_DESC_EOP BIT(30) #define CPDMA_DESC_OWNER BIT(29) #define CPDMA_DESC_EOQ BIT(28) +#define CPDMA_FROM_TO_PORT_SHIFT 16 +#define CPDMA_RX_SOURCE_PORT(__status__) \ + (((__status__) >> CPDMA_FROM_TO_PORT_SHIFT) & 0x7) #define SLIVER_SIZE 0x40 @@ -758,10 +761,11 @@ done: return 0; } -static int cpdma_process(struct cpsw_priv *priv, struct cpdma_chan *chan, +static int cpdma_process(struct cpsw_slave *slave, struct cpdma_chan *chan, void **buffer, int *len) { struct cpdma_desc *desc = chan->head; + struct cpsw_priv *priv = slave->cpsw; u32 status; if (!desc) @@ -783,6 +787,14 @@ static int cpdma_process(struct cpsw_priv *priv, struct cpdma_chan *chan, return -EBUSY; } + /* cpsw_send is cleaning finished descriptors on next send + * so we only have to check for rx channel here + */ + if (CPDMA_RX_SOURCE_PORT(status) != BIT(slave->slave_num) && + chan == &priv->rx_chan) { + return -ENOMSG; + } + chan->head = (void *)readl(&desc->hw_next); writel((u32)desc, chan->cp); @@ -917,7 +929,7 @@ static int cpsw_send(struct eth_device *edev, void *packet, int length) dev_dbg(&slave->dev, "* %s slave %d\n", __func__, slave->slave_num); /* first reap completed packets */ - while (cpdma_process(priv, &priv->tx_chan, &buffer, &len) >= 0); + while (cpdma_process(slave, &priv->tx_chan, &buffer, &len) >= 0); dev_dbg(&slave->dev, "%s: %i bytes @ 0x%p\n", __func__, length, packet); @@ -935,7 +947,7 @@ static int cpsw_recv(struct eth_device *edev) void *buffer; int len; - while (cpdma_process(priv, &priv->rx_chan, &buffer, &len) >= 0) { + while (cpdma_process(slave, &priv->rx_chan, &buffer, &len) >= 0) { dma_sync_single_for_cpu((unsigned long)buffer, len, DMA_FROM_DEVICE); net_receive(edev, buffer, len); -- cgit v1.2.3 From da4d82546a231c6384ffb3f5074dd7aeace32428 Mon Sep 17 00:00:00 2001 From: Michael Grzeschik Date: Mon, 4 May 2020 09:50:21 +0200 Subject: net: cpsw: add vlan handling for dual_emac mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit From TI am335x Reference Manual: 14.3.2.10.2 Dual Mac Mode When operating in dual mac mode the intention is to transfer packets between ports 0 and 1 and ports 0 and 2, but not between ports 1 and 2. Each CPGMAC_SL appears as a single MAC with no bridging between MAC’s. Each CPGMAC_SL has at least one unique (not the same) mac address. Dual mac mode is configured as described below: - Set the ale_vlan_aware bit in the ALE_Control register. This bit configures the ALE to process in vlan aware mode. With X can be Port 1 or Port 2: - Configure the Port X to Port 0 VLAN. Add a VLAN Table Entry with ports 0 and X as members (clear the flood masks). Add a VLAN/Unicast Address Table Entry with the PortX/0 VLAN and a port number of 0. - Select the dual mac mode on the port 0 FIFO by setting tx_in_sel[1:0] = 01 in P0_Tx_In_Ctl. Signed-off-by: Michael Grzeschik Signed-off-by: Sascha Hauer --- drivers/net/cpsw.c | 99 ++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 89 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/net/cpsw.c b/drivers/net/cpsw.c index ced77dd401..e5c0316193 100644 --- a/drivers/net/cpsw.c +++ b/drivers/net/cpsw.c @@ -164,10 +164,12 @@ enum cpsw_ale_port_state { /* ALE unicast entry flags - passed into cpsw_ale_add_ucast() */ #define ALE_SECURE 1 #define ALE_BLOCKED 2 +#define ALE_VLAN 4 struct cpsw_slave { struct cpsw_slave_regs *regs; struct cpsw_sliver_regs *sliver; + int port_vlan; int slave_num; int phy_id; phy_interface_t phy_if; @@ -261,6 +263,7 @@ static inline void cpsw_ale_set_##name(u32 *ale_entry, u32 value) \ } DEFINE_ALE_FIELD(entry_type, 60, 2) +DEFINE_ALE_FIELD(vlan_id, 48, 12) DEFINE_ALE_FIELD(mcast_state, 62, 2) DEFINE_ALE_FIELD(port_mask, 66, 3) DEFINE_ALE_FIELD(ucast_type, 62, 2) @@ -268,6 +271,10 @@ DEFINE_ALE_FIELD(port_num, 66, 2) DEFINE_ALE_FIELD(blocked, 65, 1) DEFINE_ALE_FIELD(secure, 64, 1) DEFINE_ALE_FIELD(mcast, 40, 1) +DEFINE_ALE_FIELD(vlan_untag, 24, 3) +DEFINE_ALE_FIELD(vlan_reg_mcast, 16, 3) +DEFINE_ALE_FIELD(vlan_unreg_mcast, 8, 3) +DEFINE_ALE_FIELD(vlan_member_list, 0, 3) static char ethbdaddr[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; @@ -312,6 +319,23 @@ static int cpsw_ale_write(struct cpsw_priv *priv, int idx, u32 *ale_entry) return idx; } +static int cpsw_ale_match_vlan(struct cpsw_priv *priv, u16 vid) +{ + u32 ale_entry[ALE_ENTRY_WORDS]; + int type, idx; + + for (idx = 0; idx < priv->ale_entries; idx++) { + cpsw_ale_read(priv, idx, ale_entry); + type = cpsw_ale_get_entry_type(ale_entry); + if (type != ALE_TYPE_VLAN) + continue; + if (cpsw_ale_get_vlan_id(ale_entry) == vid) + return idx; + } + + return -ENOENT; +} + static int cpsw_ale_match_addr(struct cpsw_priv *priv, u8* addr) { u32 ale_entry[ALE_ENTRY_WORDS]; @@ -376,13 +400,47 @@ static int cpsw_ale_find_ageable(struct cpsw_priv *priv) return -ENOENT; } +static int cpsw_ale_add_vlan(struct cpsw_priv *priv, u16 vid, int port_mask, + int untag, int reg_mcast, int unreg_mcast) +{ + u32 ale_entry[ALE_ENTRY_WORDS] = {0, 0, 0}; + int idx; + + idx = cpsw_ale_match_vlan(priv, vid); + if (idx >= 0) + cpsw_ale_read(priv, idx, ale_entry); + + cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_VLAN); + cpsw_ale_set_vlan_id(ale_entry, vid); + cpsw_ale_set_vlan_untag(ale_entry, untag); + cpsw_ale_set_vlan_reg_mcast(ale_entry, reg_mcast); + cpsw_ale_set_vlan_unreg_mcast(ale_entry, unreg_mcast); + cpsw_ale_set_vlan_member_list(ale_entry, port_mask); + + if (idx < 0) + idx = cpsw_ale_match_free(priv); + if (idx < 0) + idx = cpsw_ale_find_ageable(priv); + if (idx < 0) + return -ENOMEM; + + cpsw_ale_write(priv, idx, ale_entry); + return 0; +} + static int cpsw_ale_add_ucast(struct cpsw_priv *priv, u8 *addr, - int port, int flags) + int port, int flags, u16 vid) { u32 ale_entry[ALE_ENTRY_WORDS] = {0, 0, 0}; int idx; - cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_ADDR); + if (flags & ALE_VLAN) { + cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_VLAN_ADDR); + cpsw_ale_set_vlan_id(ale_entry, vid); + } else { + cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_ADDR); + } + cpsw_ale_set_addr(ale_entry, addr); cpsw_ale_set_ucast_type(ale_entry, ALE_UCAST_PERSISTANT); cpsw_ale_set_secure(ale_entry, (flags & ALE_SECURE) ? 1 : 0); @@ -401,7 +459,8 @@ static int cpsw_ale_add_ucast(struct cpsw_priv *priv, u8 *addr, return 0; } -static int cpsw_ale_add_mcast(struct cpsw_priv *priv, u8 *addr, int port_mask) +static int cpsw_ale_add_mcast(struct cpsw_priv *priv, u8 *addr, int port_mask, + int flags, u16 vid, int mcast_state) { u32 ale_entry[ALE_ENTRY_WORDS] = {0, 0, 0}; int idx, mask; @@ -410,9 +469,14 @@ static int cpsw_ale_add_mcast(struct cpsw_priv *priv, u8 *addr, int port_mask) if (idx >= 0) cpsw_ale_read(priv, idx, ale_entry); - cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_ADDR); + if (flags & ALE_VLAN) { + cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_VLAN_ADDR); + cpsw_ale_set_vlan_id(ale_entry, vid); + } else { + cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_ADDR); + } cpsw_ale_set_addr(ale_entry, addr); - cpsw_ale_set_mcast_state(ale_entry, ALE_MCAST_FWD_2); + cpsw_ale_set_mcast_state(ale_entry, mcast_state); mask = cpsw_ale_get_port_mask(ale_entry); port_mask |= mask; @@ -676,6 +740,7 @@ static inline u32 cpsw_get_slave_port(struct cpsw_priv *priv, u32 slave_num) static void cpsw_slave_init(struct cpsw_slave *slave, struct cpsw_priv *priv) { u32 slave_port; + u32 port_mask; dev_dbg(&slave->dev, "* %s\n", __func__); @@ -692,8 +757,22 @@ static void cpsw_slave_init(struct cpsw_slave *slave, struct cpsw_priv *priv) slave_port = cpsw_get_slave_port(priv, slave->slave_num); cpsw_ale_port_state(priv, slave_port, ALE_PORT_STATE_FORWARD); + port_mask = BIT(slave_port) | BIT(priv->host_port); + + /* set port_vlan to host_vlan */ + writel(BIT(slave->slave_num), &slave->regs->port_vlan); + slave->port_vlan = readl(&slave->regs->port_vlan); + slave->port_vlan &= 0xfff; + + /* add dual emac default entries */ + cpsw_ale_add_vlan(priv, slave->port_vlan, port_mask, + port_mask, port_mask, 0); /* add broadcast address */ - cpsw_ale_add_mcast(priv, ethbdaddr, 1 << slave_port); + cpsw_ale_add_mcast(priv, ethbdaddr, BIT(priv->host_port), ALE_VLAN, + slave->port_vlan, 0); + cpsw_ale_add_ucast(priv, priv->mac_addr, priv->host_port, + ALE_SECURE | ALE_VLAN, + slave->port_vlan); } static struct cpdma_desc *cpdma_desc_alloc(struct cpsw_priv *priv) @@ -839,7 +918,10 @@ static int cpsw_setup(struct device_d *dev) cpsw_ale_enable(priv, 1); cpsw_ale_clear(priv, 1); cpsw_ale_bypass(priv, 0); - cpsw_ale_vlan_aware(priv, 0); /* vlan unaware mode */ + cpsw_ale_vlan_aware(priv, 1); /* vlan aware mode */ + + /* dual mac mode in fifo */ + writel(BIT(16), &priv->host_port_regs->flow_thresh); /* setup host port priority mapping */ writel(0x76543210, &priv->host_port_regs->cpdma_tx_pri_map); @@ -853,9 +935,6 @@ static int cpsw_setup(struct device_d *dev) cpsw_ale_port_state(priv, priv->host_port, ALE_PORT_STATE_FORWARD); - cpsw_ale_add_ucast(priv, priv->mac_addr, priv->host_port, - ALE_SECURE); - cpsw_ale_add_mcast(priv, ethbdaddr, 1 << priv->host_port); /* init descriptor pool */ for (i = 0; i < NUM_DESCS; i++) { u32 val = (i == (NUM_DESCS - 1)) ? 0 : (u32)&priv->descs[i + 1]; -- cgit v1.2.3 From 25583ed994b5d4f4c0c9ff798552a3d43d51bca4 Mon Sep 17 00:00:00 2001 From: Michael Grzeschik Date: Mon, 4 May 2020 09:50:22 +0200 Subject: net: cpsw: make cpsw_send directional For dual_emac mode, the driver needs to send on the individual ports. We set the directional bits on each packet so the controller knows where to send its. From TI am335x Reference Manual: 14.3.2.10.2 Dual Mac Mode - Packets from the host (port 0) to ports 1 and 2 should be directed. Signed-off-by: Michael Grzeschik Signed-off-by: Sascha Hauer --- drivers/net/cpsw.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/cpsw.c b/drivers/net/cpsw.c index e5c0316193..b01e7ac7a8 100644 --- a/drivers/net/cpsw.c +++ b/drivers/net/cpsw.c @@ -52,6 +52,7 @@ #define CPDMA_DESC_EOP BIT(30) #define CPDMA_DESC_OWNER BIT(29) #define CPDMA_DESC_EOQ BIT(28) +#define CPDMA_DESC_TO_PORT_EN BIT(20) #define CPDMA_FROM_TO_PORT_SHIFT 16 #define CPDMA_RX_SOURCE_PORT(__status__) \ (((__status__) >> CPDMA_FROM_TO_PORT_SHIFT) & 0x7) @@ -796,7 +797,7 @@ static void cpdma_desc_free(struct cpsw_priv *priv, struct cpdma_desc *desc) } static int cpdma_submit(struct cpsw_priv *priv, struct cpdma_chan *chan, - void *buffer, int len) + void *buffer, int len, int port) { struct cpdma_desc *desc, *prev; u32 mode; @@ -810,6 +811,10 @@ static int cpdma_submit(struct cpsw_priv *priv, struct cpdma_chan *chan, mode = CPDMA_DESC_OWNER | CPDMA_DESC_SOP | CPDMA_DESC_EOP; + if (port) + mode |= CPDMA_DESC_TO_PORT_EN | + (port << CPDMA_FROM_TO_PORT_SHIFT); + writel(0, &desc->hw_next); writel((u32)buffer, &desc->hw_buffer); writel(len, &desc->hw_len); @@ -971,7 +976,7 @@ static int cpsw_setup(struct device_d *dev) /* submit rx descs */ for (i = 0; i < PKTBUFSRX - 2; i++) { ret = cpdma_submit(priv, &priv->rx_chan, NetRxPackets[i], - PKTSIZE); + PKTSIZE, 0); if (ret < 0) { dev_err(dev, "error %d submitting rx desc\n", ret); break; @@ -1013,7 +1018,8 @@ static int cpsw_send(struct eth_device *edev, void *packet, int length) dev_dbg(&slave->dev, "%s: %i bytes @ 0x%p\n", __func__, length, packet); dma_sync_single_for_device((unsigned long)packet, length, DMA_TO_DEVICE); - ret = cpdma_submit(priv, &priv->tx_chan, packet, length); + ret = cpdma_submit(priv, &priv->tx_chan, packet, + length, BIT(slave->slave_num)); dma_sync_single_for_cpu((unsigned long)packet, length, DMA_TO_DEVICE); return ret; @@ -1032,7 +1038,7 @@ static int cpsw_recv(struct eth_device *edev) net_receive(edev, buffer, len); dma_sync_single_for_device((unsigned long)buffer, len, DMA_FROM_DEVICE); - cpdma_submit(priv, &priv->rx_chan, buffer, PKTSIZE); + cpdma_submit(priv, &priv->rx_chan, buffer, PKTSIZE, 0); } return 0; -- cgit v1.2.3