Commit 600003a3 authored by David S. Miller's avatar David S. Miller

Merge branch 'sparx5-dma'

Steen Hegelund says:

====================
Adding Frame DMA functionality to Sparx5

v2:
    Removed an unused variable (proc_ctrl) from sparx5_fdma_start.

This add frame DMA functionality to the Sparx5 platform.

Until now the Sparx5 SwitchDev driver has been using register based
injection and extraction when sending frames to/from the host CPU.

With this series the Frame DMA functionality now added.

The Frame DMA is only used if the Frame DMA interrupt is configured in the
device tree; otherwise the existing register based injection and extraction
is used.

The Sparx5 has two ports that can be used for sending and receiving frames,
but there are 8 channels that can be configured: 6 for injection and 2 for
extraction.

The additional channels can be used for more advanced scenarios e.g. where
virtual cores are used, but currently the driver only uses port 0 and
channel 0 and 6 respectively.

DCB (data control block) structures are passed to the Frame DMA with
suitable information about frame start/end etc, as well as pointers to DB
(data blocks) buffers.

The Frame DMA engine can use interrupts to signal back when the frames have
been injected or extracted.

There is a limitation on the DB alignment also for injection: Block must
start on 16byte boundaries, and this is why the driver currently copies the
data to into separate buffers.

The Sparx5 switch core needs a IFH (Internal Frame Header) to pass
information from the port to the switch core, and this header is added
before injection and stripped after extraction.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents f402303b 920c293a
......@@ -471,8 +471,9 @@ switch: switch@0x600000000 {
<0x6 0x10004000 0x7fc000>,
<0x6 0x11010000 0xaf0000>;
reg-names = "cpu", "dev", "gcb";
interrupt-names = "xtr";
interrupts = <GIC_SPI 30 IRQ_TYPE_LEVEL_HIGH>;
interrupt-names = "xtr", "fdma";
interrupts = <GIC_SPI 30 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 25 IRQ_TYPE_LEVEL_HIGH>;
resets = <&reset 0>;
reset-names = "switch";
};
......
......@@ -7,4 +7,4 @@ obj-$(CONFIG_SPARX5_SWITCH) += sparx5-switch.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_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)
sparx5_board_init(sparx5);
err = sparx5_register_notifier_blocks(sparx5);
/* Start register based INJ/XTR */
/* Start Frame DMA with fallback to register based INJ/XTR */
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) {
err = devm_request_irq(sparx5->dev, sparx5->xtr_irq,
sparx5_xtr_handler, IRQF_SHARED,
......@@ -766,6 +781,7 @@ static int mchp_sparx5_probe(struct platform_device *pdev)
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");
/* Read chip ID to check CPU interface */
......@@ -824,6 +840,11 @@ static int mchp_sparx5_remove(struct platform_device *pdev)
disable_irq(sparx5->xtr_irq);
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);
/* Unregister netdevs */
sparx5_unregister_notifier_blocks(sparx5);
......
......@@ -73,8 +73,61 @@ enum sparx5_vlan_port_type {
#define XTR_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_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 {
phy_interface_t portmode;
u32 bandwidth;
......@@ -167,6 +220,10 @@ struct sparx5 {
bool sd_sgpio_remapping;
/* Register based inj/xtr */
int xtr_irq;
/* Frame DMA */
int fdma_irq;
struct sparx5_rx rx;
struct sparx5_tx tx;
};
/* sparx5_switchdev.c */
......@@ -174,11 +231,23 @@ int sparx5_register_notifier_blocks(struct sparx5 *sparx5);
void sparx5_unregister_notifier_blocks(struct sparx5 *sparx5);
/* 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);
int sparx5_port_xmit_impl(struct sk_buff *skb, struct net_device *dev);
int sparx5_manual_injection_mode(struct sparx5 *sparx5);
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 */
void sparx5_mact_pull_work(struct work_struct *work);
int sparx5_mact_learn(struct sparx5 *sparx5, int port,
......
......@@ -20,11 +20,7 @@
#define INJ_TIMEOUT_NS 50000
struct frame_info {
int src_port;
};
static void sparx5_xtr_flush(struct sparx5 *sparx5, u8 grp)
void sparx5_xtr_flush(struct sparx5 *sparx5, u8 grp)
{
/* Start 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)
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;
......@@ -224,7 +220,10 @@ int sparx5_port_xmit_impl(struct sk_buff *skb, struct net_device *dev)
struct sparx5 *sparx5 = port->sparx5;
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) {
stats->tx_bytes += skb->len;
......
......@@ -596,7 +596,7 @@ static int sparx5_port_max_tags_set(struct sparx5 *sparx5,
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 urg = 672000;
......
......@@ -89,5 +89,6 @@ int sparx5_get_port_status(struct sparx5 *sparx5,
struct sparx5_port_status *status);
void sparx5_port_enable(struct sparx5_port *port, bool enable);
int sparx5_port_fwd_urg(struct sparx5 *sparx5, u32 speed);
#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