Commit 10615907 authored by Steen Hegelund's avatar Steen Hegelund Committed by David S. Miller

net: sparx5: switchdev: adding frame DMA functionality

This add frame DMA functionality to the Sparx5 platform.

Ethernet frames can be extracted or injected autonomously to or from the
device’s DDR3/DDR3L memory and/or PCIe memory space. Linked list data
structures in memory are used for injecting or extracting Ethernet frames.
The FDMA generates interrupts when frame extraction or injection is done
and when the linked lists need updating.

The FDMA implements two extraction channels, one per switch core port
towards the VCore CPU system and a total of six injection channels.
Extraction channels are mapped one-to-one to the CPU ports, while injection
channels can be individually assigned to any CPU port.

- FDMA channel 0 through 5 corresponds to CPU port 0 injection direction
  FDMA_CH_CFG[channel].CH_INJ_PORT is set to 0.
- FDMA channel 0 through 5 corresponds to CPU port 1 injection direction when
  FDMA_CH_CFG[channel].CH_INJ_PORT is set to 1.
- FDMA channel 6 corresponds to CPU port 0 extraction direction.
- FDMA channel 7 corresponds to CPU port 1 extraction direction.

The FDMA implements a strict priority scheme among channels. Extraction
channels are prioritized over injection channels and secondarily channels
with higher channel number are prioritized over channels with lower number.
On the other hand, ports are being served on an equal-bandwidth principle
both on injection and extraction directions.  The equal-bandwidth principle
will not force an equal bandwidth. Instead, it ensures that the ports
perform at their best considering the operating conditions.

When more than one injection channel is enabled for injection on the same
CPU port, priority determines which channel can inject data. Ownership
is re-arbitrated on frame boundaries.

The FDMA processes linked lists of DMA Control Block Structures (DCBs). The
DCBs have the same basic structure for both injection and extraction. A DCB
must be placed on a 64-bit word-aligned address in memory. Each DCB has a
per-channel configurable amount of associated data blocks in memory, where
the frame data is stored.

The data blocks that are used by extraction channels must be placed on
64-bit word aligned addresses in memory, and their length must be a
multiple of 128 bytes.

A DCB carries the pointer to the next DCB of the linked list, the INFO word
which holds information for the DCB, and a pair of status word and memory
pointer for every data block that it is associated with.
Signed-off-by: default avatarSteen Hegelund <steen.hegelund@microchip.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent f402303b
...@@ -7,4 +7,4 @@ obj-$(CONFIG_SPARX5_SWITCH) += sparx5-switch.o ...@@ -7,4 +7,4 @@ obj-$(CONFIG_SPARX5_SWITCH) += sparx5-switch.o
sparx5-switch-objs := sparx5_main.o sparx5_packet.o \ sparx5-switch-objs := sparx5_main.o sparx5_packet.o \
sparx5_netdev.o sparx5_phylink.o sparx5_port.o sparx5_mactable.o sparx5_vlan.o \ sparx5_netdev.o sparx5_phylink.o sparx5_port.o sparx5_mactable.o sparx5_vlan.o \
sparx5_switchdev.o sparx5_calendar.o sparx5_ethtool.o sparx5_switchdev.o sparx5_calendar.o sparx5_ethtool.o sparx5_fdma.o
This diff is collapsed.
...@@ -640,8 +640,23 @@ static int sparx5_start(struct sparx5 *sparx5) ...@@ -640,8 +640,23 @@ static int sparx5_start(struct sparx5 *sparx5)
sparx5_board_init(sparx5); sparx5_board_init(sparx5);
err = sparx5_register_notifier_blocks(sparx5); err = sparx5_register_notifier_blocks(sparx5);
/* Start register based INJ/XTR */ /* Start Frame DMA with fallback to register based INJ/XTR */
err = -ENXIO; err = -ENXIO;
if (sparx5->fdma_irq >= 0) {
if (GCB_CHIP_ID_REV_ID_GET(sparx5->chip_id) > 0)
err = devm_request_threaded_irq(sparx5->dev,
sparx5->fdma_irq,
NULL,
sparx5_fdma_handler,
IRQF_ONESHOT,
"sparx5-fdma", sparx5);
if (!err)
err = sparx5_fdma_start(sparx5);
if (err)
sparx5->fdma_irq = -ENXIO;
} else {
sparx5->fdma_irq = -ENXIO;
}
if (err && sparx5->xtr_irq >= 0) { if (err && sparx5->xtr_irq >= 0) {
err = devm_request_irq(sparx5->dev, sparx5->xtr_irq, err = devm_request_irq(sparx5->dev, sparx5->xtr_irq,
sparx5_xtr_handler, IRQF_SHARED, sparx5_xtr_handler, IRQF_SHARED,
...@@ -766,6 +781,7 @@ static int mchp_sparx5_probe(struct platform_device *pdev) ...@@ -766,6 +781,7 @@ static int mchp_sparx5_probe(struct platform_device *pdev)
sparx5->base_mac[5] = 0; sparx5->base_mac[5] = 0;
} }
sparx5->fdma_irq = platform_get_irq_byname(sparx5->pdev, "fdma");
sparx5->xtr_irq = platform_get_irq_byname(sparx5->pdev, "xtr"); sparx5->xtr_irq = platform_get_irq_byname(sparx5->pdev, "xtr");
/* Read chip ID to check CPU interface */ /* Read chip ID to check CPU interface */
...@@ -824,6 +840,11 @@ static int mchp_sparx5_remove(struct platform_device *pdev) ...@@ -824,6 +840,11 @@ static int mchp_sparx5_remove(struct platform_device *pdev)
disable_irq(sparx5->xtr_irq); disable_irq(sparx5->xtr_irq);
sparx5->xtr_irq = -ENXIO; sparx5->xtr_irq = -ENXIO;
} }
if (sparx5->fdma_irq) {
disable_irq(sparx5->fdma_irq);
sparx5->fdma_irq = -ENXIO;
}
sparx5_fdma_stop(sparx5);
sparx5_cleanup_ports(sparx5); sparx5_cleanup_ports(sparx5);
/* Unregister netdevs */ /* Unregister netdevs */
sparx5_unregister_notifier_blocks(sparx5); sparx5_unregister_notifier_blocks(sparx5);
......
...@@ -73,8 +73,61 @@ enum sparx5_vlan_port_type { ...@@ -73,8 +73,61 @@ enum sparx5_vlan_port_type {
#define XTR_QUEUE 0 #define XTR_QUEUE 0
#define INJ_QUEUE 0 #define INJ_QUEUE 0
#define FDMA_DCB_MAX 64
#define FDMA_RX_DCB_MAX_DBS 15
#define FDMA_TX_DCB_MAX_DBS 1
struct sparx5; struct sparx5;
struct sparx5_db_hw {
u64 dataptr;
u64 status;
};
struct sparx5_rx_dcb_hw {
u64 nextptr;
u64 info;
struct sparx5_db_hw db[FDMA_RX_DCB_MAX_DBS];
};
struct sparx5_tx_dcb_hw {
u64 nextptr;
u64 info;
struct sparx5_db_hw db[FDMA_TX_DCB_MAX_DBS];
};
/* Frame DMA receive state:
* For each DB, there is a SKB, and the skb data pointer is mapped in
* the DB. Once a frame is received the skb is given to the upper layers
* and a new skb is added to the dcb.
* When the db_index reached FDMA_RX_DCB_MAX_DBS the DB is reused.
*/
struct sparx5_rx {
struct sparx5_rx_dcb_hw *dcb_entries;
struct sparx5_rx_dcb_hw *last_entry;
struct sk_buff *skb[FDMA_DCB_MAX][FDMA_RX_DCB_MAX_DBS];
int db_index;
int dcb_index;
dma_addr_t dma;
struct napi_struct napi;
u32 channel_id;
struct net_device *ndev;
u64 packets;
};
/* Frame DMA transmit state:
* DCBs are chained using the DCBs nextptr field.
*/
struct sparx5_tx {
struct sparx5_tx_dcb_hw *curr_entry;
struct sparx5_tx_dcb_hw *first_entry;
struct list_head db_list;
dma_addr_t dma;
u32 channel_id;
u64 packets;
u64 dropped;
};
struct sparx5_port_config { struct sparx5_port_config {
phy_interface_t portmode; phy_interface_t portmode;
u32 bandwidth; u32 bandwidth;
...@@ -167,6 +220,10 @@ struct sparx5 { ...@@ -167,6 +220,10 @@ struct sparx5 {
bool sd_sgpio_remapping; bool sd_sgpio_remapping;
/* Register based inj/xtr */ /* Register based inj/xtr */
int xtr_irq; int xtr_irq;
/* Frame DMA */
int fdma_irq;
struct sparx5_rx rx;
struct sparx5_tx tx;
}; };
/* sparx5_switchdev.c */ /* sparx5_switchdev.c */
...@@ -174,11 +231,23 @@ int sparx5_register_notifier_blocks(struct sparx5 *sparx5); ...@@ -174,11 +231,23 @@ int sparx5_register_notifier_blocks(struct sparx5 *sparx5);
void sparx5_unregister_notifier_blocks(struct sparx5 *sparx5); void sparx5_unregister_notifier_blocks(struct sparx5 *sparx5);
/* sparx5_packet.c */ /* sparx5_packet.c */
struct frame_info {
int src_port;
};
void sparx5_xtr_flush(struct sparx5 *sparx5, u8 grp);
void sparx5_ifh_parse(u32 *ifh, struct frame_info *info);
irqreturn_t sparx5_xtr_handler(int irq, void *_priv); irqreturn_t sparx5_xtr_handler(int irq, void *_priv);
int sparx5_port_xmit_impl(struct sk_buff *skb, struct net_device *dev); int sparx5_port_xmit_impl(struct sk_buff *skb, struct net_device *dev);
int sparx5_manual_injection_mode(struct sparx5 *sparx5); int sparx5_manual_injection_mode(struct sparx5 *sparx5);
void sparx5_port_inj_timer_setup(struct sparx5_port *port); void sparx5_port_inj_timer_setup(struct sparx5_port *port);
/* sparx5_fdma.c */
int sparx5_fdma_start(struct sparx5 *sparx5);
int sparx5_fdma_stop(struct sparx5 *sparx5);
int sparx5_fdma_xmit(struct sparx5 *sparx5, u32 *ifh, struct sk_buff *skb);
irqreturn_t sparx5_fdma_handler(int irq, void *args);
/* sparx5_mactable.c */ /* sparx5_mactable.c */
void sparx5_mact_pull_work(struct work_struct *work); void sparx5_mact_pull_work(struct work_struct *work);
int sparx5_mact_learn(struct sparx5 *sparx5, int port, int sparx5_mact_learn(struct sparx5 *sparx5, int port,
......
...@@ -20,11 +20,7 @@ ...@@ -20,11 +20,7 @@
#define INJ_TIMEOUT_NS 50000 #define INJ_TIMEOUT_NS 50000
struct frame_info { void sparx5_xtr_flush(struct sparx5 *sparx5, u8 grp)
int src_port;
};
static void sparx5_xtr_flush(struct sparx5 *sparx5, u8 grp)
{ {
/* Start flush */ /* Start flush */
spx5_wr(QS_XTR_FLUSH_FLUSH_SET(BIT(grp)), sparx5, QS_XTR_FLUSH); spx5_wr(QS_XTR_FLUSH_FLUSH_SET(BIT(grp)), sparx5, QS_XTR_FLUSH);
...@@ -36,7 +32,7 @@ static void sparx5_xtr_flush(struct sparx5 *sparx5, u8 grp) ...@@ -36,7 +32,7 @@ static void sparx5_xtr_flush(struct sparx5 *sparx5, u8 grp)
spx5_wr(0, sparx5, QS_XTR_FLUSH); spx5_wr(0, sparx5, QS_XTR_FLUSH);
} }
static void sparx5_ifh_parse(u32 *ifh, struct frame_info *info) void sparx5_ifh_parse(u32 *ifh, struct frame_info *info)
{ {
u8 *xtr_hdr = (u8 *)ifh; u8 *xtr_hdr = (u8 *)ifh;
...@@ -224,7 +220,10 @@ int sparx5_port_xmit_impl(struct sk_buff *skb, struct net_device *dev) ...@@ -224,7 +220,10 @@ int sparx5_port_xmit_impl(struct sk_buff *skb, struct net_device *dev)
struct sparx5 *sparx5 = port->sparx5; struct sparx5 *sparx5 = port->sparx5;
int ret; int ret;
ret = sparx5_inject(sparx5, port->ifh, skb, dev); if (sparx5->fdma_irq > 0)
ret = sparx5_fdma_xmit(sparx5, port->ifh, skb);
else
ret = sparx5_inject(sparx5, port->ifh, skb, dev);
if (ret == NETDEV_TX_OK) { if (ret == NETDEV_TX_OK) {
stats->tx_bytes += skb->len; stats->tx_bytes += skb->len;
......
...@@ -596,7 +596,7 @@ static int sparx5_port_max_tags_set(struct sparx5 *sparx5, ...@@ -596,7 +596,7 @@ static int sparx5_port_max_tags_set(struct sparx5 *sparx5,
return 0; return 0;
} }
static int sparx5_port_fwd_urg(struct sparx5 *sparx5, u32 speed) int sparx5_port_fwd_urg(struct sparx5 *sparx5, u32 speed)
{ {
u32 clk_period_ps = 1600; /* 625Mhz for now */ u32 clk_period_ps = 1600; /* 625Mhz for now */
u32 urg = 672000; u32 urg = 672000;
......
...@@ -89,5 +89,6 @@ int sparx5_get_port_status(struct sparx5 *sparx5, ...@@ -89,5 +89,6 @@ int sparx5_get_port_status(struct sparx5 *sparx5,
struct sparx5_port_status *status); struct sparx5_port_status *status);
void sparx5_port_enable(struct sparx5_port *port, bool enable); void sparx5_port_enable(struct sparx5_port *port, bool enable);
int sparx5_port_fwd_urg(struct sparx5 *sparx5, u32 speed);
#endif /* __SPARX5_PORT_H__ */ #endif /* __SPARX5_PORT_H__ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment