// SPDX-License-Identifier: GPL-2.0
/*
* Texas Instruments Ethernet Switch Driver
*
* Copyright (C) 2019 Texas Instruments
*/
#include <linux/bpf.h>
#include <linux/bpf_trace.h>
#include <linux/if_ether.h>
#include <linux/if_vlan.h>
#include <linux/kmemleak.h>
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/net_tstamp.h>
#include <linux/of.h>
#include <linux/phy.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/skbuff.h>
#include <net/page_pool/helpers.h>
#include <net/pkt_cls.h>
#include <net/pkt_sched.h>
#include "cpsw.h"
#include "cpts.h"
#include "cpsw_ale.h"
#include "cpsw_priv.h"
#include "cpsw_sl.h"
#include "davinci_cpdma.h"
#define CPTS_N_ETX_TS 4
int (*cpsw_slave_index)(struct cpsw_common *cpsw, struct cpsw_priv *priv);
void cpsw_intr_enable(struct cpsw_common *cpsw)
{
writel_relaxed(0xFF, &cpsw->wr_regs->tx_en);
writel_relaxed(0xFF, &cpsw->wr_regs->rx_en);
cpdma_ctlr_int_ctrl(cpsw->dma, true);
}
void cpsw_intr_disable(struct cpsw_common *cpsw)
{
writel_relaxed(0, &cpsw->wr_regs->tx_en);
writel_relaxed(0, &cpsw->wr_regs->rx_en);
cpdma_ctlr_int_ctrl(cpsw->dma, false);
}
void cpsw_tx_handler(void *token, int len, int status)
{
struct cpsw_meta_xdp *xmeta;
struct xdp_frame *xdpf;
struct net_device *ndev;
struct netdev_queue *txq;
struct sk_buff *skb;
int ch;
if (cpsw_is_xdpf_handle(token)) {
xdpf = cpsw_handle_to_xdpf(token);
xmeta = (void *)xdpf + CPSW_XMETA_OFFSET;
ndev = xmeta->ndev;
ch = xmeta->ch;
xdp_return_frame(xdpf);
} else {
skb = token;
ndev = skb->dev;
ch = skb_get_queue_mapping(skb);
cpts_tx_timestamp(ndev_to_cpsw(ndev)->cpts, skb);
dev_kfree_skb_any(skb);
}
/* Check whether the queue is stopped due to stalled tx dma, if the
* queue is stopped then start the queue as we have free desc for tx
*/
txq = netdev_get_tx_queue(ndev, ch);
if (unlikely(netif_tx_queue_stopped(txq)))
netif_tx_wake_queue(txq);
ndev->stats.tx_packets++;
ndev->stats.tx_bytes += len;
}
irqreturn_t cpsw_tx_interrupt(int irq, void *dev_id)
{
struct cpsw_common *cpsw = dev_id;
writel(0, &cpsw->wr_regs->tx_en);
cpdma_ctlr_eoi(cpsw->dma, CPDMA_EOI_TX);
if (cpsw->quirk_irq) {
disable_irq_nosync(cpsw->irqs_table[1]);
cpsw->tx_irq_disabled = true;
}
napi_schedule(&cpsw->napi_tx);
return IRQ_HANDLED;
}
irqreturn_t cpsw_rx_interrupt(int irq, void *dev_id)
{
struct cpsw_common *cpsw = dev_id;
writel(0, &cpsw->wr_regs->rx_en);
cpdma_ctlr_eoi(cpsw->dma, CPDMA_EOI_RX);
if (cpsw->quirk_irq) {
disable_irq_nosync(cpsw->irqs_table[0]);
cpsw->rx_irq_disabled = true;
}
napi_schedule(&cpsw->napi_rx);
return IRQ_HANDLED;
}
irqreturn_t cpsw_misc_interrupt(int irq, void *dev_id)
{
struct cpsw_common *cpsw = dev_id;
writel(0, &cpsw->wr_regs->misc_en);
cpdma_ctlr_eoi(cpsw->dma, CPDMA_EOI_MISC);
cpts_misc_interrupt(cpsw->cpts);
writel(0x10, &cpsw->wr_regs->misc_en);
return IRQ_HANDLED;
}
int cpsw_tx_mq_poll(struct napi_struct *napi_tx, int budget)
{
struct cpsw_common *cpsw = napi_to_cpsw(napi_tx);
int num_tx, cur_budget, ch;
u32 ch_map;
struct cpsw_vector *txv;
/* process every unprocessed channel */
ch_map = cpdma_ctrl_txchs_state(cpsw->dma);
for (ch = 0, num_tx = 0; ch_map & 0xff; ch_map <<= 1, ch++) {
if (!(ch_map & 0x80))
continue;
txv = &cpsw->txv[ch];
if (unlikely(txv->budget > budget - num_tx))
cur_budget = budget - num_tx;
else
cur_budget = txv->budget;
num_tx += cpdma_chan_process(txv->ch, cur_budget);
if (num_tx >= budget)
break;
}
if (num_tx < budget) {
napi_complete(napi_tx);
writel(0xff, &cpsw->wr_regs->tx_en);
}
return num_tx;
}
int cpsw_tx_poll(struct napi_struct *napi_tx, int budget)
{
struct cpsw_common *cpsw = napi_to_cpsw(napi_tx);
int num_tx;
num_tx = cpdma_chan_process(cpsw->txv[0].ch, budget);
if (num_tx < budget) {
napi_complete(napi_tx);
writel(0xff, &cpsw->wr_regs->tx_en);
if (cpsw->tx_irq_disabled) {
cpsw->tx_irq_disabled = false;
enable_irq(cpsw->irqs_table[1]);
}
}
return num_tx;
}
int cpsw_rx_mq_poll(struct napi_struct *napi_rx, int budget)
{
struct cpsw_common *cpsw = napi_to_cpsw(napi_rx);
int num_rx, cur_budget, ch;
u32 ch_map;
struct cpsw_vector *rxv;
/* process every unprocessed channel */
ch_map = cpdma_ctrl_rxchs_state(cpsw->dma);
for (ch = 0, num_rx = 0; ch_map; ch_map >>= 1, ch++) {
if (!(ch_map & 0x01))
continue;
rxv = &cpsw->rxv[ch];
if (unlikely(rxv->budget > budget - num_rx))
cur_budget = budget - num_rx;
else
cur_budget = rxv->budget;
num_rx += cpdma_chan_process(rxv->ch, cur_budget);
if (num_rx >= budget)
break;
}
if (num_rx < budget) {
napi_complete_done(napi_rx, num_rx);
writel(0xff, &cpsw->wr_regs->rx_en);
}
return num_rx;
}
int cpsw_rx_poll(struct napi_struct *napi_rx, int budge
|