staticstruct { int pkt_size; int buf_num;
} mvpp2_pools[MVPP2_BM_POOLS_NUM];
/* The prototype is added here to be used in start_dev when using ACPI. This * will be removed once phylink is used for all modes (dt+ACPI).
*/ staticvoid mvpp2_acpi_start(struct mvpp2_port *port);
/* These accessors should be used to access: * * - per-thread registers, where each thread has its own copy of the * register. * * MVPP2_BM_VIRT_ALLOC_REG * MVPP2_BM_ADDR_HIGH_ALLOC * MVPP22_BM_ADDR_HIGH_RLS_REG * MVPP2_BM_VIRT_RLS_REG * MVPP2_ISR_RX_TX_CAUSE_REG * MVPP2_ISR_RX_TX_MASK_REG * MVPP2_TXQ_NUM_REG * MVPP2_AGGR_TXQ_UPDATE_REG * MVPP2_TXQ_RSVD_REQ_REG * MVPP2_TXQ_RSVD_RSLT_REG * MVPP2_TXQ_SENT_REG * MVPP2_RXQ_NUM_REG * * - global registers that must be accessed through a specific thread * window, because they are related to an access to a per-thread * register * * MVPP2_BM_PHY_ALLOC_REG (related to MVPP2_BM_VIRT_ALLOC_REG) * MVPP2_BM_PHY_RLS_REG (related to MVPP2_BM_VIRT_RLS_REG) * MVPP2_RXQ_THRESH_REG (related to MVPP2_RXQ_NUM_REG) * MVPP2_RXQ_DESC_ADDR_REG (related to MVPP2_RXQ_NUM_REG) * MVPP2_RXQ_DESC_SIZE_REG (related to MVPP2_RXQ_NUM_REG) * MVPP2_RXQ_INDEX_REG (related to MVPP2_RXQ_NUM_REG) * MVPP2_TXQ_PENDING_REG (related to MVPP2_TXQ_NUM_REG) * MVPP2_TXQ_DESC_ADDR_REG (related to MVPP2_TXQ_NUM_REG) * MVPP2_TXQ_DESC_SIZE_REG (related to MVPP2_TXQ_NUM_REG) * MVPP2_TXQ_INDEX_REG (related to MVPP2_TXQ_NUM_REG) * MVPP2_TXQ_PENDING_REG (related to MVPP2_TXQ_NUM_REG) * MVPP2_TXQ_PREF_BUF_REG (related to MVPP2_TXQ_NUM_REG) * MVPP2_TXQ_PREF_BUF_REG (related to MVPP2_TXQ_NUM_REG)
*/ staticvoid mvpp2_thread_write(struct mvpp2 *priv, unsignedint thread,
u32 offset, u32 data)
{
writel(data, priv->swth_base[thread] + offset);
}
/* Get number of maximum RXQ */ staticint mvpp2_get_nrxqs(struct mvpp2 *priv)
{ unsignedint nrxqs;
if (priv->hw_version >= MVPP22 && queue_mode == MVPP2_QDIST_SINGLE_MODE) return 1;
/* According to the PPv2.2 datasheet and our experiments on * PPv2.1, RX queues have an allocation granularity of 4 (when * more than a single one on PPv2.2). * Round up to nearest multiple of 4.
*/
nrxqs = (num_possible_cpus() + 3) & ~0x3; if (nrxqs > MVPP2_PORT_MAX_RXQ)
nrxqs = MVPP2_PORT_MAX_RXQ;
return nrxqs;
}
/* Get number of physical egress port */ staticinlineint mvpp2_egress_port(struct mvpp2_port *port)
{ return MVPP2_MAX_TCONT + port->id;
}
/* Get number of physical TXQ */ staticinlineint mvpp2_txq_phys(int port, int txq)
{ return (MVPP2_MAX_TCONT + port) * MVPP2_MAX_TXQ + txq;
}
/* Returns a struct page if page_pool is set, otherwise a buffer */ staticvoid *mvpp2_frag_alloc(conststruct mvpp2_bm_pool *pool, struct page_pool *page_pool)
{ if (page_pool) return page_pool_dev_alloc_pages(page_pool);
if (likely(pool->frag_size <= PAGE_SIZE)) return netdev_alloc_frag(pool->frag_size);
val = mvpp2_read(priv, MVPP2_BM_POOL_CTRL_REG(bm_pool->id));
val |= MVPP2_BM_START_MASK;
val &= ~MVPP2_BM_LOW_THRESH_MASK;
val &= ~MVPP2_BM_HIGH_THRESH_MASK;
/* Set 8 Pools BPPI threshold for MVPP23 */ if (priv->hw_version == MVPP23) {
val |= MVPP2_BM_LOW_THRESH_VALUE(MVPP23_BM_BPPI_LOW_THRESH);
val |= MVPP2_BM_HIGH_THRESH_VALUE(MVPP23_BM_BPPI_HIGH_THRESH);
} else {
val |= MVPP2_BM_LOW_THRESH_VALUE(MVPP2_BM_BPPI_LOW_THRESH);
val |= MVPP2_BM_HIGH_THRESH_VALUE(MVPP2_BM_BPPI_HIGH_THRESH);
}
if (sizeof(dma_addr_t) == 8)
*dma_addr |= (u64)dma_addr_highbits << 32;
if (sizeof(phys_addr_t) == 8)
*phys_addr |= (u64)phys_addr_highbits << 32;
}
put_cpu();
}
/* Free all buffers from the pool */ staticvoid mvpp2_bm_bufs_free(struct device *dev, struct mvpp2 *priv, struct mvpp2_bm_pool *bm_pool, int buf_num)
{ struct page_pool *pp = NULL; int i;
if (buf_num > bm_pool->buf_num) {
WARN(1, "Pool does not have so many bufs pool(%d) bufs(%d)\n",
bm_pool->id, buf_num);
buf_num = bm_pool->buf_num;
}
if (priv->percpu_pools)
pp = priv->page_pool[bm_pool->id];
for (i = 0; i < buf_num; i++) {
dma_addr_t buf_dma_addr;
phys_addr_t buf_phys_addr; void *data;
/* Check buffer counters after free */
buf_num = mvpp2_check_hw_buf_num(priv, bm_pool); if (buf_num) {
WARN(1, "cannot free all buffers in pool %d, buf_num left %d\n",
bm_pool->id, bm_pool->buf_num); return 0;
}
val = mvpp2_read(priv, MVPP2_BM_POOL_CTRL_REG(bm_pool->id));
val |= MVPP2_BM_STOP_MASK;
mvpp2_write(priv, MVPP2_BM_POOL_CTRL_REG(bm_pool->id), val);
if (priv->percpu_pools) {
page_pool_destroy(priv->page_pool[bm_pool->id]);
priv->page_pool[bm_pool->id] = NULL;
}
staticint mvpp2_bm_pools_init(struct device *dev, struct mvpp2 *priv)
{ int i, err, size, poolnum = MVPP2_BM_POOLS_NUM; struct mvpp2_bm_pool *bm_pool;
if (priv->percpu_pools)
poolnum = mvpp2_get_nrxqs(priv) * 2;
/* Create all pools with maximum size */
size = MVPP2_BM_POOL_SIZE_MAX; for (i = 0; i < poolnum; i++) {
bm_pool = &priv->bm_pools[i];
bm_pool->id = i;
err = mvpp2_bm_pool_create(dev, priv, bm_pool, size); if (err) goto err_unroll_pools;
mvpp2_bm_pool_bufsize_set(priv, bm_pool, 0);
} return 0;
err_unroll_pools:
dev_err(dev, "failed to create BM pool %d, size %d\n", i, size); for (i = i - 1; i >= 0; i--)
mvpp2_bm_pool_destroy(dev, priv, &priv->bm_pools[i]); return err;
}
/* Routine enable PPv23 8 pool mode */ staticvoid mvpp23_bm_set_8pool_mode(struct mvpp2 *priv)
{ int val;
val = mvpp2_read(priv, MVPP22_BM_POOL_BASE_ADDR_HIGH_REG);
val |= MVPP23_BM_8POOL_MODE;
mvpp2_write(priv, MVPP22_BM_POOL_BASE_ADDR_HIGH_REG, val);
}
/* Cleanup pool before actual initialization in the OS */ staticvoid mvpp2_bm_pool_cleanup(struct mvpp2 *priv, int pool_id)
{ unsignedint thread = mvpp2_cpu_to_thread(priv, get_cpu());
u32 val; int i;
/* Drain the BM from all possible residues left by firmware */ for (i = 0; i < MVPP2_BM_POOL_SIZE_MAX; i++)
mvpp2_thread_read(priv, thread, MVPP2_BM_PHY_ALLOC_REG(pool_id));
put_cpu();
/* Stop the BM pool */
val = mvpp2_read(priv, MVPP2_BM_POOL_CTRL_REG(pool_id));
val |= MVPP2_BM_STOP_MASK;
mvpp2_write(priv, MVPP2_BM_POOL_CTRL_REG(pool_id), val);
}
if (priv->percpu_pools)
poolnum = mvpp2_get_nrxqs(priv) * 2;
/* Clean up the pool state in case it contains stale state */ for (i = 0; i < poolnum; i++)
mvpp2_bm_pool_cleanup(priv, i);
if (priv->percpu_pools) { for (i = 0; i < priv->port_count; i++) {
port = priv->port_list[i]; if (port->xdp_prog) {
dma_dir = DMA_BIDIRECTIONAL; break;
}
}
for (i = 0; i < poolnum; i++) { /* the pool in use */ int pn = i / (poolnum / 2);
priv->page_pool[i] =
mvpp2_create_page_pool(dev,
mvpp2_pools[pn].buf_num,
mvpp2_pools[pn].pkt_size,
dma_dir); if (IS_ERR(priv->page_pool[i])) { int j;
/* Remove Flow control enable bit to prevent race between FW and Kernel * If Flow control was enabled, it would be re-enabled.
*/
val = mvpp2_cm3_read(port->priv, MSS_FC_COM_REG);
cm3_state = (val & FLOW_CONTROL_ENABLE_BIT);
val &= ~FLOW_CONTROL_ENABLE_BIT;
mvpp2_cm3_write(port->priv, MSS_FC_COM_REG, val);
/* Set same Flow control for all RXQs */ for (q = 0; q < port->nrxqs; q++) { /* Set stop and start Flow control RXQ thresholds */
val = MSS_THRESHOLD_START;
val |= (MSS_THRESHOLD_STOP << MSS_RXQ_TRESH_STOP_OFFS);
mvpp2_cm3_write(port->priv, MSS_RXQ_TRESH_REG(q, fq), val);
val = mvpp2_cm3_read(port->priv, MSS_RXQ_ASS_REG(q, fq)); /* Set RXQ port ID */
val &= ~(MSS_RXQ_ASS_PORTID_MASK << MSS_RXQ_ASS_Q_BASE(q, fq));
val |= (port->id << MSS_RXQ_ASS_Q_BASE(q, fq));
val &= ~(MSS_RXQ_ASS_HOSTID_MASK << (MSS_RXQ_ASS_Q_BASE(q, fq)
+ MSS_RXQ_ASS_HOSTID_OFFS));
/* Calculate RXQ host ID: * In Single queue mode: Host ID equal to Host ID used for * shared RX interrupt * In Multi queue mode: Host ID equal to number of * RXQ ID / number of CoS queues * In Single resource mode: Host ID always equal to 0
*/ if (queue_mode == MVPP2_QDIST_SINGLE_MODE)
host_id = port->nqvecs; elseif (queue_mode == MVPP2_QDIST_MULTI_MODE)
host_id = q; else
host_id = 0;
/* Set RXQ host ID */
val |= (host_id << (MSS_RXQ_ASS_Q_BASE(q, fq)
+ MSS_RXQ_ASS_HOSTID_OFFS));
/* Notify Firmware that Flow control config space ready for update */
val = mvpp2_cm3_read(port->priv, MSS_FC_COM_REG);
val |= FLOW_CONTROL_UPDATE_COMMAND_BIT;
val |= cm3_state;
mvpp2_cm3_write(port->priv, MSS_FC_COM_REG, val);
/* Remove Flow control enable bit to prevent race between FW and Kernel * If Flow control was enabled, it would be re-enabled.
*/
val = mvpp2_cm3_read(port->priv, MSS_FC_COM_REG);
cm3_state = (val & FLOW_CONTROL_ENABLE_BIT);
val &= ~FLOW_CONTROL_ENABLE_BIT;
mvpp2_cm3_write(port->priv, MSS_FC_COM_REG, val);
/* Disable Flow control for all RXQs */ for (q = 0; q < port->nrxqs; q++) { /* Set threshold 0 to disable Flow control */
val = 0;
val |= (0 << MSS_RXQ_TRESH_STOP_OFFS);
mvpp2_cm3_write(port->priv, MSS_RXQ_TRESH_REG(q, fq), val);
val = mvpp2_cm3_read(port->priv, MSS_RXQ_ASS_REG(q, fq));
val &= ~(MSS_RXQ_ASS_PORTID_MASK << MSS_RXQ_ASS_Q_BASE(q, fq));
val &= ~(MSS_RXQ_ASS_HOSTID_MASK << (MSS_RXQ_ASS_Q_BASE(q, fq)
+ MSS_RXQ_ASS_HOSTID_OFFS));
/* Notify Firmware that Flow control config space ready for update */
val = mvpp2_cm3_read(port->priv, MSS_FC_COM_REG);
val |= FLOW_CONTROL_UPDATE_COMMAND_BIT;
val |= cm3_state;
mvpp2_cm3_write(port->priv, MSS_FC_COM_REG, val);
/* Remove Flow control enable bit to prevent race between FW and Kernel * If Flow control were enabled, it would be re-enabled.
*/
val = mvpp2_cm3_read(port->priv, MSS_FC_COM_REG);
cm3_state = (val & FLOW_CONTROL_ENABLE_BIT);
val &= ~FLOW_CONTROL_ENABLE_BIT;
mvpp2_cm3_write(port->priv, MSS_FC_COM_REG, val);
/* Check if BM pool should be enabled/disable */ if (en) { /* Set BM pool start and stop thresholds per port */
val = mvpp2_cm3_read(port->priv, MSS_BUF_POOL_REG(pool->id));
val |= MSS_BUF_POOL_PORT_OFFS(port->id);
val &= ~MSS_BUF_POOL_START_MASK;
val |= (MSS_THRESHOLD_START << MSS_BUF_POOL_START_OFFS);
val &= ~MSS_BUF_POOL_STOP_MASK;
val |= MSS_THRESHOLD_STOP;
mvpp2_cm3_write(port->priv, MSS_BUF_POOL_REG(pool->id), val);
} else { /* Remove BM pool from the port */
val = mvpp2_cm3_read(port->priv, MSS_BUF_POOL_REG(pool->id));
val &= ~MSS_BUF_POOL_PORT_OFFS(port->id);
/* Zero BM pool start and stop thresholds to disable pool * flow control if pool empty (not used by any port)
*/ if (!pool->buf_num) {
val &= ~MSS_BUF_POOL_START_MASK;
val &= ~MSS_BUF_POOL_STOP_MASK;
}
/* Notify Firmware that Flow control config space ready for update */
val = mvpp2_cm3_read(port->priv, MSS_FC_COM_REG);
val |= FLOW_CONTROL_UPDATE_COMMAND_BIT;
val |= cm3_state;
mvpp2_cm3_write(port->priv, MSS_FC_COM_REG, val);
/* disable/enable flow control for BM pool on all ports */ staticvoid mvpp2_bm_pool_update_priv_fc(struct mvpp2 *priv, bool en)
{ struct mvpp2_port *port; int i, j;
for (i = 0; i < priv->port_count; i++) {
port = priv->port_list[i]; if (port->priv->percpu_pools) { for (j = 0; j < port->nrxqs; j++)
mvpp2_bm_pool_update_fc(port, &port->priv->bm_pools[j],
port->tx_fc & en);
} else {
mvpp2_bm_pool_update_fc(port, port->pool_long, port->tx_fc & en);
mvpp2_bm_pool_update_fc(port, port->pool_short, port->tx_fc & en);
}
}
}
staticint mvpp2_enable_global_fc(struct mvpp2 *priv)
{ int val, timeout = 0;
/* Enable global flow control. In this stage global * flow control enabled, but still disabled per port.
*/
val = mvpp2_cm3_read(priv, MSS_FC_COM_REG);
val |= FLOW_CONTROL_ENABLE_BIT;
mvpp2_cm3_write(priv, MSS_FC_COM_REG, val);
/* Check if Firmware running and disable FC if not*/
val |= FLOW_CONTROL_UPDATE_COMMAND_BIT;
mvpp2_cm3_write(priv, MSS_FC_COM_REG, val);
while (timeout < MSS_FC_MAX_TIMEOUT) {
val = mvpp2_cm3_read(priv, MSS_FC_COM_REG);
if (!(val & FLOW_CONTROL_UPDATE_COMMAND_BIT)) return 0;
usleep_range(10, 20);
timeout++;
}
priv->global_tx_fc = false; return -EOPNOTSUPP;
}
/* Release buffer to BM */ staticinlinevoid mvpp2_bm_pool_put(struct mvpp2_port *port, int pool,
dma_addr_t buf_dma_addr,
phys_addr_t buf_phys_addr)
{ unsignedint thread = mvpp2_cpu_to_thread(port->priv, get_cpu()); unsignedlong flags = 0;
if (test_bit(thread, &port->priv->lock_map))
spin_lock_irqsave(&port->bm_lock[thread], flags);
if (port->priv->hw_version >= MVPP22) {
u32 val = 0;
if (sizeof(dma_addr_t) == 8)
val |= upper_32_bits(buf_dma_addr) &
MVPP22_BM_ADDR_HIGH_PHYS_RLS_MASK;
if (sizeof(phys_addr_t) == 8)
val |= (upper_32_bits(buf_phys_addr)
<< MVPP22_BM_ADDR_HIGH_VIRT_RLS_SHIFT) &
MVPP22_BM_ADDR_HIGH_VIRT_RLS_MASK;
/* MVPP2_BM_VIRT_RLS_REG is not interpreted by HW, and simply * returned in the "cookie" field of the RX * descriptor. Instead of storing the virtual address, we * store the physical address
*/
mvpp2_thread_write_relaxed(port->priv, thread,
MVPP2_BM_VIRT_RLS_REG, buf_phys_addr);
mvpp2_thread_write_relaxed(port->priv, thread,
MVPP2_BM_PHY_RLS_REG(pool), buf_dma_addr);
if (test_bit(thread, &port->priv->lock_map))
spin_unlock_irqrestore(&port->bm_lock[thread], flags);
put_cpu();
}
/* Allocate buffers for the pool */ staticint mvpp2_bm_bufs_add(struct mvpp2_port *port, struct mvpp2_bm_pool *bm_pool, int buf_num)
{ int i, buf_size, total_size;
dma_addr_t dma_addr;
phys_addr_t phys_addr; struct page_pool *pp = NULL; void *buf;
if (port->priv->percpu_pools &&
bm_pool->pkt_size > MVPP2_BM_LONG_PKT_SIZE) {
netdev_err(port->dev, "attempted to use jumbo frames with per-cpu pools"); return 0;
}
if (buf_num < 0 ||
(buf_num + bm_pool->buf_num > bm_pool->size)) {
netdev_err(port->dev, "cannot allocate %d buffers for pool %d\n",
buf_num, bm_pool->id); return 0;
}
if (port->priv->percpu_pools)
pp = port->priv->page_pool[bm_pool->id]; for (i = 0; i < buf_num; i++) {
buf = mvpp2_buf_alloc(port, bm_pool, pp, &dma_addr,
&phys_addr, GFP_KERNEL); if (!buf) break;
netdev_dbg(port->dev, "pool %d: %d of %d buffers added\n",
bm_pool->id, i, buf_num); return i;
}
/* Notify the driver that BM pool is being used as specific type and return the * pool pointer on success
*/ staticstruct mvpp2_bm_pool *
mvpp2_bm_pool_use(struct mvpp2_port *port, unsigned pool, int pkt_size)
{ struct mvpp2_bm_pool *new_pool = &port->priv->bm_pools[pool]; int num;
if ((port->priv->percpu_pools && pool > mvpp2_get_nrxqs(port->priv) * 2) ||
(!port->priv->percpu_pools && pool >= MVPP2_BM_POOLS_NUM)) {
netdev_err(port->dev, "Invalid pool %d\n", pool); return NULL;
}
/* Allocate buffers in case BM pool is used as long pool, but packet * size doesn't match MTU or BM pool hasn't being used yet
*/ if (new_pool->pkt_size == 0) { int pkts_num;
/* Set default buffer number or free all the buffers in case * the pool is not empty
*/
pkts_num = new_pool->buf_num; if (pkts_num == 0) { if (port->priv->percpu_pools) { if (pool < port->nrxqs)
pkts_num = mvpp2_pools[MVPP2_BM_SHORT].buf_num; else
pkts_num = mvpp2_pools[MVPP2_BM_LONG].buf_num;
} else {
pkts_num = mvpp2_pools[pool].buf_num;
}
} else {
mvpp2_bm_bufs_free(port->dev->dev.parent,
port->priv, new_pool, pkts_num);
}
staticstruct mvpp2_bm_pool *
mvpp2_bm_pool_use_percpu(struct mvpp2_port *port, int type, unsignedint pool, int pkt_size)
{ struct mvpp2_bm_pool *new_pool = &port->priv->bm_pools[pool]; int num;
if (pool > port->nrxqs * 2) {
netdev_err(port->dev, "Invalid pool %d\n", pool); return NULL;
}
/* Allocate buffers in case BM pool is used as long pool, but packet * size doesn't match MTU or BM pool hasn't being used yet
*/ if (new_pool->pkt_size == 0) { int pkts_num;
/* Set default buffer number or free all the buffers in case * the pool is not empty
*/
pkts_num = new_pool->buf_num; if (pkts_num == 0)
pkts_num = mvpp2_pools[type].buf_num; else
mvpp2_bm_bufs_free(port->dev->dev.parent,
port->priv, new_pool, pkts_num);
/* Initialize pools for swf, shared buffers variant */ staticint mvpp2_swf_bm_pool_init_shared(struct mvpp2_port *port)
{ enum mvpp2_bm_pool_log_num long_log_pool, short_log_pool; int rxq;
/* If port pkt_size is higher than 1518B: * HW Long pool - SW Jumbo pool, HW Short pool - SW Long pool * else: HW Long pool - SW Long pool, HW Short pool - SW Short pool
*/ if (port->pkt_size > MVPP2_BM_LONG_PKT_SIZE) {
long_log_pool = MVPP2_BM_JUMBO;
short_log_pool = MVPP2_BM_LONG;
} else {
long_log_pool = MVPP2_BM_LONG;
short_log_pool = MVPP2_BM_SHORT;
}
if (!port->pool_long) {
port->pool_long =
mvpp2_bm_pool_use(port, long_log_pool,
mvpp2_pools[long_log_pool].pkt_size); if (!port->pool_long) return -ENOMEM;
/* Initialize pools for swf, percpu buffers variant */ staticint mvpp2_swf_bm_pool_init_percpu(struct mvpp2_port *port)
{ struct mvpp2_bm_pool *bm_pool; int i;
for (i = 0; i < port->nrxqs; i++) {
bm_pool = mvpp2_bm_pool_use_percpu(port, MVPP2_BM_SHORT, i,
mvpp2_pools[MVPP2_BM_SHORT].pkt_size); if (!bm_pool) return -ENOMEM;
bm_pool->port_map |= BIT(port->id);
mvpp2_rxq_short_pool_set(port, i, bm_pool->id);
}
for (i = 0; i < port->nrxqs; i++) {
bm_pool = mvpp2_bm_pool_use_percpu(port, MVPP2_BM_LONG, i + port->nrxqs,
mvpp2_pools[MVPP2_BM_LONG].pkt_size); if (!bm_pool) return -ENOMEM;
bm_pool->port_map |= BIT(port->id);
mvpp2_rxq_long_pool_set(port, i, bm_pool->id);
}
/* Update L4 checksum when jumbo enable/disable on port. * Only port 0 supports hardware checksum offload due to * the Tx FIFO size limitation. * Also, don't set NETIF_F_HW_CSUM because L3_offset in TX descriptor * has 7 bits, so the maximum L3 offset is 128.
*/ if (new_long_pool == MVPP2_BM_JUMBO && port->id != 0) {
port->dev->features &= ~csums;
port->dev->hw_features &= ~csums;
} else {
port->dev->features |= csums;
port->dev->hw_features |= csums;
}
}
staticint mvpp2_bm_update_mtu(struct net_device *dev, int mtu)
{ struct mvpp2_port *port = netdev_priv(dev); enum mvpp2_bm_pool_log_num new_long_pool; int pkt_size = MVPP2_RX_PKT_SIZE(mtu);
if (port->priv->percpu_pools) goto out_set;
/* If port MTU is higher than 1518B: * HW Long pool - SW Jumbo pool, HW Short pool - SW Long pool * else: HW Long pool - SW Long pool, HW Short pool - SW Short pool
*/ if (pkt_size > MVPP2_BM_LONG_PKT_SIZE)
new_long_pool = MVPP2_BM_JUMBO; else
new_long_pool = MVPP2_BM_LONG;
if (new_long_pool != port->pool_long->id) { if (port->tx_fc) { if (pkt_size > MVPP2_BM_LONG_PKT_SIZE)
mvpp2_bm_pool_update_fc(port,
port->pool_short, false); else
mvpp2_bm_pool_update_fc(port, port->pool_long, false);
}
/* Remove port from old short & long pool */
port->pool_long = mvpp2_bm_pool_use(port, port->pool_long->id,
port->pool_long->pkt_size);
port->pool_long->port_map &= ~BIT(port->id);
port->pool_long = NULL;
/* Mask the current thread's Rx/Tx interrupts * Called by on_each_cpu(), guaranteed to run with migration disabled, * using smp_processor_id() is OK.
*/ staticvoid mvpp2_interrupts_mask(void *arg)
{ struct mvpp2_port *port = arg; int cpu = smp_processor_id();
u32 thread;
/* If the thread isn't used, don't do anything */ if (cpu > port->priv->nthreads) return;
/* Unmask the current thread's Rx/Tx interrupts. * Called by on_each_cpu(), guaranteed to run with migration disabled, * using smp_processor_id() is OK.
*/ staticvoid mvpp2_interrupts_unmask(void *arg)
{ struct mvpp2_port *port = arg; int cpu = smp_processor_id();
u32 val, thread;
/* If the thread isn't used, don't do anything */ if (cpu >= port->priv->nthreads) return;
thread = mvpp2_cpu_to_thread(port->priv, cpu);
val = MVPP2_CAUSE_MISC_SUM_MASK |
MVPP2_CAUSE_RXQ_OCCUP_DESC_ALL_MASK(port->priv->hw_version); if (port->has_tx_irqs)
val |= MVPP2_CAUSE_TXQ_OCCUP_DESC_ALL_MASK;
regmap_read(priv->sysctrl_base, GENCONF_PORT_CTRL0, &val);
val |= GENCONF_PORT_CTRL0_BUS_WIDTH_SELECT;
regmap_write(priv->sysctrl_base, GENCONF_PORT_CTRL0, val);
regmap_read(priv->sysctrl_base, GENCONF_CTRL0, &val); if (port->gop_id == 2) {
val |= GENCONF_CTRL0_PORT2_RGMII;
} elseif (port->gop_id == 3) {
val |= GENCONF_CTRL0_PORT3_RGMII_MII;
/* According to the specification, GENCONF_CTRL0_PORT3_RGMII * should be set to 1 for RGMII and 0 for MII. However, tests * show that it is the other way around. This is also what * U-Boot does for mvpp2, so it is assumed to be correct.
*/ if (port->phy_interface == PHY_INTERFACE_MODE_MII)
val |= GENCONF_CTRL0_PORT3_RGMII; else
val &= ~GENCONF_CTRL0_PORT3_RGMII;
}
regmap_write(priv->sysctrl_base, GENCONF_CTRL0, val);
}
val = readl(fca + MVPP22_FCA_CONTROL_REG);
val &= ~MVPP22_FCA_ENABLE_PERIODIC; if (en)
val |= MVPP22_FCA_ENABLE_PERIODIC;
writel(val, fca + MVPP22_FCA_CONTROL_REG);
}
/* Set Flow Control timer x100 faster than pause quanta to ensure that link * partner won't send traffic if port is in XOFF mode.
*/ staticvoid mvpp22_gop_fca_set_periodic_timer(struct mvpp2_port *port)
{
u32 timer;
switch (interface) { case PHY_INTERFACE_MODE_MII: case PHY_INTERFACE_MODE_RGMII: case PHY_INTERFACE_MODE_RGMII_ID: case PHY_INTERFACE_MODE_RGMII_RXID: case PHY_INTERFACE_MODE_RGMII_TXID: if (!mvpp2_port_supports_rgmii(port)) goto invalid_conf;
mvpp22_gop_init_rgmii(port); break; case PHY_INTERFACE_MODE_SGMII: case PHY_INTERFACE_MODE_1000BASEX: case PHY_INTERFACE_MODE_2500BASEX:
mvpp22_gop_init_sgmii(port); break; case PHY_INTERFACE_MODE_5GBASER: case PHY_INTERFACE_MODE_10GBASER: if (!mvpp2_port_supports_xlg(port)) goto invalid_conf;
mvpp22_gop_init_10gkr(port); break; default: goto unsupported_conf;
}
if (phy_interface_mode_is_rgmii(port->phy_interface) ||
phy_interface_mode_is_8023z(port->phy_interface) ||
port->phy_interface == PHY_INTERFACE_MODE_SGMII) { /* Enable the GMAC link status irq for this port */
val = readl(port->base + MVPP22_GMAC_INT_SUM_MASK);
val |= MVPP22_GMAC_INT_SUM_MASK_LINK_STAT;
writel(val, port->base + MVPP22_GMAC_INT_SUM_MASK);
}
if (mvpp2_port_supports_xlg(port)) { /* Enable the XLG/GIG irqs for this port */
val = readl(port->base + MVPP22_XLG_EXT_INT_MASK); if (mvpp2_is_xlg(port->phy_interface))
val |= MVPP22_XLG_EXT_INT_MASK_XLG; else
val |= MVPP22_XLG_EXT_INT_MASK_GIG;
writel(val, port->base + MVPP22_XLG_EXT_INT_MASK);
}
}
if (port->phylink ||
phy_interface_mode_is_rgmii(port->phy_interface) ||
phy_interface_mode_is_8023z(port->phy_interface) ||
port->phy_interface == PHY_INTERFACE_MODE_SGMII) {
val = readl(port->base + MVPP22_GMAC_INT_MASK);
val |= MVPP22_GMAC_INT_MASK_LINK_STAT;
writel(val, port->base + MVPP22_GMAC_INT_MASK);
}
if (mvpp2_port_supports_xlg(port)) {
val = readl(port->base + MVPP22_XLG_INT_MASK);
val |= MVPP22_XLG_INT_MASK_LINK;
writel(val, port->base + MVPP22_XLG_INT_MASK);
/* Sets the PHY mode of the COMPHY (which configures the serdes lanes). * * The PHY mode used by the PPv2 driver comes from the network subsystem, while * the one given to the COMPHY comes from the generic PHY subsystem. Hence they * differ. * * The COMPHY configures the serdes lanes regardless of the actual use of the * lanes by the physical layer. This is why configurations like * "PPv2 (2500BaseX) - COMPHY (2500SGMII)" are valid.
*/ staticint mvpp22_comphy_init(struct mvpp2_port *port,
phy_interface_t interface)
{ int ret;
if (!port->comphy) return 0;
ret = phy_set_mode_ext(port->comphy, PHY_MODE_ETHERNET, interface); if (ret) return ret;
if (state->speed == 1000)
val |= MVPP2_GMAC_GMII_LB_EN_MASK; else
val &= ~MVPP2_GMAC_GMII_LB_EN_MASK;
if (phy_interface_mode_is_8023z(state->interface) ||
state->interface == PHY_INTERFACE_MODE_SGMII)
val |= MVPP2_GMAC_PCS_LB_EN_MASK; else
val &= ~MVPP2_GMAC_PCS_LB_EN_MASK;
val = readl(port->stats_base + counter->offset); if (counter->reg_is_64b)
val += (u64)readl(port->stats_base + counter->offset + 4) << 32;
return val;
}
/* Some counters are accessed indirectly by first writing an index to * MVPP2_CTRS_IDX. The index can represent various resources depending on the * register we access, it can be a hit counter for some classification tables, * a counter specific to a rxq, a txq or a buffer pool.
*/ static u32 mvpp2_read_index(struct mvpp2 *priv, u32 index, u32 reg)
{
mvpp2_write(priv, MVPP2_CTRS_IDX, index); return mvpp2_read(priv, reg);
}
/* Due to the fact that software statistics and hardware statistics are, by * design, incremented at different moments in the chain of packet processing, * it is very likely that incoming packets could have been dropped after being * counted by hardware but before reaching software statistics (most probably * multicast packets), and in the opposite way, during transmission, FCS bytes * are added in between as well as TSO skb will be split and header bytes added. * Hence, statistics gathered from userspace with ifconfig (software) and * ethtool (hardware) cannot be compared.
*/ staticconststruct mvpp2_ethtool_counter mvpp2_ethtool_mib_regs[] = {
{ MVPP2_MIB_GOOD_OCTETS_RCVD, "good_octets_received", true },
{ MVPP2_MIB_BAD_OCTETS_RCVD, "bad_octets_received" },
{ MVPP2_MIB_CRC_ERRORS_SENT, "crc_errors_sent" },
{ MVPP2_MIB_UNICAST_FRAMES_RCVD, "unicast_frames_received" },
{ MVPP2_MIB_BROADCAST_FRAMES_RCVD, "broadcast_frames_received" },
{ MVPP2_MIB_MULTICAST_FRAMES_RCVD, "multicast_frames_received" },
{ MVPP2_MIB_FRAMES_64_OCTETS, "frames_64_octets" },
{ MVPP2_MIB_FRAMES_65_TO_127_OCTETS, "frames_65_to_127_octet" },
{ MVPP2_MIB_FRAMES_128_TO_255_OCTETS, "frames_128_to_255_octet" },
{ MVPP2_MIB_FRAMES_256_TO_511_OCTETS, "frames_256_to_511_octet" },
{ MVPP2_MIB_FRAMES_512_TO_1023_OCTETS, "frames_512_to_1023_octet" },
{ MVPP2_MIB_FRAMES_1024_TO_MAX_OCTETS, "frames_1024_to_max_octet" },
{ MVPP2_MIB_GOOD_OCTETS_SENT, "good_octets_sent", true },
{ MVPP2_MIB_UNICAST_FRAMES_SENT, "unicast_frames_sent" },
{ MVPP2_MIB_MULTICAST_FRAMES_SENT, "multicast_frames_sent" },
{ MVPP2_MIB_BROADCAST_FRAMES_SENT, "broadcast_frames_sent" },
{ MVPP2_MIB_FC_SENT, "fc_sent" },
{ MVPP2_MIB_FC_RCVD, "fc_received" },
{ MVPP2_MIB_RX_FIFO_OVERRUN, "rx_fifo_overrun" },
{ MVPP2_MIB_UNDERSIZE_RCVD, "undersize_received" },
{ MVPP2_MIB_FRAGMENTS_RCVD, "fragments_received" },
{ MVPP2_MIB_OVERSIZE_RCVD, "oversize_received" },
{ MVPP2_MIB_JABBER_RCVD, "jabber_received" },
{ MVPP2_MIB_MAC_RCV_ERROR, "mac_receive_error" },
{ MVPP2_MIB_BAD_CRC_EVENT, "bad_crc_event" },
{ MVPP2_MIB_COLLISION, "collision" },
{ MVPP2_MIB_LATE_COLLISION, "late_collision" },
};
staticvoid mvpp2_read_stats(struct mvpp2_port *port)
{ struct mvpp2_pcpu_stats xdp_stats = {}; conststruct mvpp2_ethtool_counter *s;
u64 *pstats; int i, q;
pstats = port->ethtool_stats;
for (i = 0; i < ARRAY_SIZE(mvpp2_ethtool_mib_regs); i++)
*pstats++ += mvpp2_read_count(port, &mvpp2_ethtool_mib_regs[i]);
for (i = 0; i < ARRAY_SIZE(mvpp2_ethtool_port_regs); i++)
*pstats++ += mvpp2_read(port->priv,
mvpp2_ethtool_port_regs[i].offset +
4 * port->id);
for (q = 0; q < port->ntxqs; q++) for (i = 0; i < ARRAY_SIZE(mvpp2_ethtool_txq_regs); i++)
*pstats++ += mvpp2_read_index(port->priv,
MVPP22_CTRS_TX_CTR(port->id, q),
mvpp2_ethtool_txq_regs[i].offset);
/* Rxqs are numbered from 0 from the user standpoint, but not from the * driver's. We need to add the port->first_rxq offset.
*/ for (q = 0; q < port->nrxqs; q++) for (i = 0; i < ARRAY_SIZE(mvpp2_ethtool_rxq_regs); i++)
*pstats++ += mvpp2_read_index(port->priv,
port->first_rxq + q,
mvpp2_ethtool_rxq_regs[i].offset);
/* No need to read again the counters right after this function if it * was called asynchronously by the user (ie. use of ethtool).
*/
cancel_delayed_work(&port->stats_work);
queue_delayed_work(port->priv->stats_queue, &port->stats_work,
MVPP2_MIB_COUNTERS_STATS_DELAY);
/* Update statistics for the given port, then take the lock to avoid * concurrent accesses on the ethtool_stats structure during its copy.
*/
mvpp2_gather_hw_statistics(&port->stats_work.work);
switch (interface) { case PHY_INTERFACE_MODE_5GBASER: case PHY_INTERFACE_MODE_10GBASER:
val = readl(mpcs + MVPP22_MPCS_CLK_RESET);
val |= MAC_CLK_RESET_MAC | MAC_CLK_RESET_SD_RX |
MAC_CLK_RESET_SD_TX;
val &= ~MVPP22_MPCS_CLK_RESET_DIV_SET;
writel(val, mpcs + MVPP22_MPCS_CLK_RESET); break; case PHY_INTERFACE_MODE_XAUI: case PHY_INTERFACE_MODE_RXAUI:
val = readl(xpcs + MVPP22_XPCS_CFG0);
writel(val | MVPP22_XPCS_CFG0_RESET_DIS, xpcs + MVPP22_XPCS_CFG0); break; default: break;
}
}
/* Change maximum receive size of the port */ staticinlinevoid mvpp2_gmac_max_rx_size_set(struct mvpp2_port *port)
{
u32 val;
val = readl(port->base + MVPP2_GMAC_CTRL_0_REG);
val &= ~MVPP2_GMAC_MAX_RX_SIZE_MASK;
val |= (((port->pkt_size - MVPP2_MH_SIZE) / 2) <<
MVPP2_GMAC_MAX_RX_SIZE_OFFS);
writel(val, port->base + MVPP2_GMAC_CTRL_0_REG);
}
/* Change maximum receive size of the port */ staticinlinevoid mvpp2_xlg_max_rx_size_set(struct mvpp2_port *port)
{
u32 val;
val = readl(port->base + MVPP22_XLG_CTRL1_REG);
val &= ~MVPP22_XLG_CTRL1_FRAMESIZELIMIT_MASK;
val |= ((port->pkt_size - MVPP2_MH_SIZE) / 2) <<
MVPP22_XLG_CTRL1_FRAMESIZELIMIT_OFFS;
writel(val, port->base + MVPP22_XLG_CTRL1_REG);
}
/* Set defaults to the MVPP2 port */ staticvoid mvpp2_defaults_set(struct mvpp2_port *port)
{ int tx_port_num, val, queue, lrxq;
if (port->priv->hw_version == MVPP21) { /* Update TX FIFO MIN Threshold */
val = readl(port->base + MVPP2_GMAC_PORT_FIFO_CFG_1_REG);
val &= ~MVPP2_GMAC_TX_FIFO_MIN_TH_ALL_MASK; /* Min. TX threshold must be less than minimal packet length */
val |= MVPP2_GMAC_TX_FIFO_MIN_TH_MASK(64 - 4 - 2);
writel(val, port->base + MVPP2_GMAC_PORT_FIFO_CFG_1_REG);
}
/* Set TXQ scheduling to Round-Robin */
mvpp2_write(port->priv, MVPP2_TXP_SCHED_FIXED_PRIO_REG, 0);
/* Close bandwidth for all queues */ for (queue = 0; queue < MVPP2_MAX_TXQ; queue++)
mvpp2_write(port->priv,
MVPP2_TXQ_SCHED_TOKEN_CNTR_REG(queue), 0);
/* Set refill period to 1 usec, refill tokens * and bucket size to maximum
*/
mvpp2_write(port->priv, MVPP2_TXP_SCHED_PERIOD_REG,
port->priv->tclk / USEC_PER_SEC);
val = mvpp2_read(port->priv, MVPP2_TXP_SCHED_REFILL_REG);
val &= ~MVPP2_TXP_REFILL_PERIOD_ALL_MASK;
val |= MVPP2_TXP_REFILL_PERIOD_MASK(1);
val |= MVPP2_TXP_REFILL_TOKENS_ALL_MASK;
mvpp2_write(port->priv, MVPP2_TXP_SCHED_REFILL_REG, val);
val = MVPP2_TXP_TOKEN_SIZE_MAX;
mvpp2_write(port->priv, MVPP2_TXP_SCHED_TOKEN_SIZE_REG, val);
/* Set MaximumLowLatencyPacketSize value to 256 */
mvpp2_write(port->priv, MVPP2_RX_CTRL_REG(port->id),
MVPP2_RX_USE_PSEUDO_FOR_CSUM_MASK |
MVPP2_RX_LOW_LATENCY_PKT_SIZE(256));
for (lrxq = 0; lrxq < port->nrxqs; lrxq++) {
queue = port->rxqs[lrxq]->id;
val = mvpp2_read(port->priv, MVPP2_RXQ_CONFIG_REG(queue));
val &= ~MVPP2_RXQ_DISABLE_MASK;
mvpp2_write(port->priv, MVPP2_RXQ_CONFIG_REG(queue), val);
}
}
staticvoid mvpp2_ingress_disable(struct mvpp2_port *port)
{
u32 val; int lrxq, queue;
for (lrxq = 0; lrxq < port->nrxqs; lrxq++) {
queue = port->rxqs[lrxq]->id;
val = mvpp2_read(port->priv, MVPP2_RXQ_CONFIG_REG(queue));
val |= MVPP2_RXQ_DISABLE_MASK;
mvpp2_write(port->priv, MVPP2_RXQ_CONFIG_REG(queue), val);
}
}
/* Enable transmit via physical egress queue * - HW starts take descriptors from DRAM
*/ staticvoid mvpp2_egress_enable(struct mvpp2_port *port)
{
u32 qmap; int queue; int tx_port_num = mvpp2_egress_port(port);
/* Disable transmit via physical egress queue * - HW doesn't take descriptors from DRAM
*/ staticvoid mvpp2_egress_disable(struct mvpp2_port *port)
{
u32 reg_data; int delay; int tx_port_num = mvpp2_egress_port(port);
/* Issue stop command for active channels only */
mvpp2_write(port->priv, MVPP2_TXP_SCHED_PORT_INDEX_REG, tx_port_num);
reg_data = (mvpp2_read(port->priv, MVPP2_TXP_SCHED_Q_CMD_REG)) &
MVPP2_TXP_SCHED_ENQ_MASK; if (reg_data != 0)
mvpp2_write(port->priv, MVPP2_TXP_SCHED_Q_CMD_REG,
(reg_data << MVPP2_TXP_SCHED_DISQ_OFFSET));
/* Wait for all Tx activity to terminate. */
delay = 0; do { if (delay >= MVPP2_TX_DISABLE_TIMEOUT_MSEC) {
netdev_warn(port->dev, "Tx stop timed out, status=0x%08x\n",
reg_data); break;
}
mdelay(1);
delay++;
/* Check port TX Command register that all * Tx queues are stopped
*/
reg_data = mvpp2_read(port->priv, MVPP2_TXP_SCHED_Q_CMD_REG);
} while (reg_data & MVPP2_TXP_SCHED_ENQ_MASK);
}
/* Rx descriptors helper methods */
/* Get number of Rx descriptors occupied by received packets */ staticinlineint
mvpp2_rxq_received(struct mvpp2_port *port, int rxq_id)
{
u32 val = mvpp2_read(port->priv, MVPP2_RXQ_STATUS_REG(rxq_id));
return val & MVPP2_RXQ_OCCUPIED_MASK;
}
/* Update Rx queue status with the number of occupied and available * Rx descriptor slots.
*/ staticinlinevoid
mvpp2_rxq_status_update(struct mvpp2_port *port, int rxq_id, int used_count, int free_count)
{ /* Decrement the number of used descriptors and increment count * increment the number of free descriptors.
*/
u32 val = used_count | (free_count << MVPP2_RXQ_NUM_NEW_OFFSET);
/* Get pointer to next RX descriptor to be processed by SW */ staticinlinestruct mvpp2_rx_desc *
mvpp2_rxq_next_desc_get(struct mvpp2_rx_queue *rxq)
{ int rx_desc = rxq->next_desc_to_proc;
/* Get pointer to next Tx descriptor to be processed (send) by HW */ staticstruct mvpp2_tx_desc *
mvpp2_txq_next_desc_get(struct mvpp2_tx_queue *txq)
{ int tx_desc = txq->next_desc_to_proc;
/* Update HW with number of aggregated Tx descriptors to be sent * * Called only from mvpp2_tx(), so migration is disabled, using * smp_processor_id() is OK.
*/ staticvoid mvpp2_aggr_txq_pend_desc_add(struct mvpp2_port *port, int pending)
{ /* aggregated access - relevant TXQ number is written in TX desc */
mvpp2_thread_write(port->priv,
mvpp2_cpu_to_thread(port->priv, smp_processor_id()),
MVPP2_AGGR_TXQ_UPDATE_REG, pending);
}
/* Check if there are enough free descriptors in aggregated txq. * If not, update the number of occupied descriptors and repeat the check. * * Called only from mvpp2_tx(), so migration is disabled, using * smp_processor_id() is OK.
*/ staticint mvpp2_aggr_desc_num_check(struct mvpp2_port *port, struct mvpp2_tx_queue *aggr_txq, int num)
{ if ((aggr_txq->count + num) > MVPP2_AGGR_TXQ_SIZE) { /* Update number of occupied aggregated Tx descriptors */ unsignedint thread =
mvpp2_cpu_to_thread(port->priv, smp_processor_id());
u32 val = mvpp2_read_relaxed(port->priv,
MVPP2_AGGR_TXQ_STATUS_REG(thread));
aggr_txq->count = val & MVPP2_AGGR_TXQ_PENDING_MASK;
/* Reserved Tx descriptors allocation request * * Called only from mvpp2_txq_reserved_desc_num_proc(), itself called * only by mvpp2_tx(), so migration is disabled, using * smp_processor_id() is OK.
*/ staticint mvpp2_txq_alloc_reserved_desc(struct mvpp2_port *port, struct mvpp2_tx_queue *txq, int num)
{ unsignedint thread = mvpp2_cpu_to_thread(port->priv, smp_processor_id()); struct mvpp2 *priv = port->priv;
u32 val;
val = mvpp2_thread_read_relaxed(priv, thread, MVPP2_TXQ_RSVD_RSLT_REG);
return val & MVPP2_TXQ_RSVD_RSLT_MASK;
}
/* Check if there are enough reserved descriptors for transmission. * If not, request chunk of reserved descriptors and check again.
*/ staticint mvpp2_txq_reserved_desc_num_proc(struct mvpp2_port *port, struct mvpp2_tx_queue *txq, struct mvpp2_txq_pcpu *txq_pcpu, int num)
{ int req, desc_count; unsignedint thread;
if (txq_pcpu->reserved_num >= num) return 0;
/* Not enough descriptors reserved! Update the reserved descriptor * count and check again.
*/
desc_count = 0; /* Compute total of used descriptors */ for (thread = 0; thread < port->priv->nthreads; thread++) { struct mvpp2_txq_pcpu *txq_pcpu_aux;
/* Get number of sent descriptors and decrement counter. * The number of sent descriptors is returned. * Per-thread access * * Called only from mvpp2_txq_done(), called from mvpp2_tx() * (migration disabled) and from the TX completion tasklet (migration * disabled) so using smp_processor_id() is OK.
*/ staticinlineint mvpp2_txq_sent_desc_proc(struct mvpp2_port *port, struct mvpp2_tx_queue *txq)
{
u32 val;
/* Reading status reg resets transmitted descriptor counter */
val = mvpp2_thread_read_relaxed(port->priv,
mvpp2_cpu_to_thread(port->priv, smp_processor_id()),
MVPP2_TXQ_SENT_REG(txq->id));
/* Called through on_each_cpu(), so runs on all CPUs, with migration * disabled, therefore using smp_processor_id() is OK.
*/ staticvoid mvpp2_txq_sent_counter_clear(void *arg)
{ struct mvpp2_port *port = arg; int queue;
/* If the thread isn't used, don't do anything */ if (smp_processor_id() >= port->priv->nthreads) return;
for (queue = 0; queue < port->ntxqs; queue++) { int id = port->txqs[queue]->id;
/* Set MTU */
val = mvpp2_read(port->priv, MVPP2_TXP_SCHED_MTU_REG);
val &= ~MVPP2_TXP_MTU_MAX;
val |= mtu;
mvpp2_write(port->priv, MVPP2_TXP_SCHED_MTU_REG, val);
/* TXP token size and all TXQs token size must be larger that MTU */
val = mvpp2_read(port->priv, MVPP2_TXP_SCHED_TOKEN_SIZE_REG);
size = val & MVPP2_TXP_TOKEN_SIZE_MAX; if (size < mtu) {
size = mtu;
val &= ~MVPP2_TXP_TOKEN_SIZE_MAX;
val |= size;
mvpp2_write(port->priv, MVPP2_TXP_SCHED_TOKEN_SIZE_REG, val);
}
for (txq = 0; txq < port->ntxqs; txq++) {
val = mvpp2_read(port->priv,
MVPP2_TXQ_SCHED_TOKEN_SIZE_REG(txq));
size = val & MVPP2_TXQ_TOKEN_SIZE_MAX;
if (size < mtu) {
size = mtu;
val &= ~MVPP2_TXQ_TOKEN_SIZE_MAX;
val |= size;
mvpp2_write(port->priv,
MVPP2_TXQ_SCHED_TOKEN_SIZE_REG(txq),
val);
}
}
}
/* Set the number of non-occupied descriptors threshold */ staticvoid mvpp2_set_rxq_free_tresh(struct mvpp2_port *port, struct mvpp2_rx_queue *rxq)
{
u32 val;
val = mvpp2_read(port->priv, MVPP2_RXQ_THRESH_REG);
val &= ~MVPP2_RXQ_NON_OCCUPIED_MASK;
val |= MSS_THRESHOLD_STOP << MVPP2_RXQ_NON_OCCUPIED_OFFSET;
mvpp2_write(port->priv, MVPP2_RXQ_THRESH_REG, val);
}
/* Set the number of packets that will be received before Rx interrupt * will be generated by HW.
*/ staticvoid mvpp2_rx_pkts_coal_set(struct mvpp2_port *port, struct mvpp2_rx_queue *rxq)
{ unsignedint thread = mvpp2_cpu_to_thread(port->priv, get_cpu());
if (rxq->pkts_coal > MVPP2_OCCUPIED_THRESH_MASK)
rxq->pkts_coal = MVPP2_OCCUPIED_THRESH_MASK;
/* For some reason in the LSP this is done on each CPU. Why ? */ staticvoid mvpp2_tx_pkts_coal_set(struct mvpp2_port *port, struct mvpp2_tx_queue *txq)
{ unsignedint thread;
u32 val;
if (txq->done_pkts_coal > MVPP2_TXQ_THRESH_MASK)
txq->done_pkts_coal = MVPP2_TXQ_THRESH_MASK;
/* Set the time delay in usec before Rx interrupt */ staticvoid mvpp2_rx_time_coal_set(struct mvpp2_port *port, struct mvpp2_rx_queue *rxq)
{ unsignedlong freq = port->priv->tclk;
u32 val = mvpp2_usec_to_cycles(rxq->time_coal, freq);
if (val > MVPP2_MAX_ISR_RX_THRESHOLD) {
rxq->time_coal =
mvpp2_cycles_to_usec(MVPP2_MAX_ISR_RX_THRESHOLD, freq);
/* re-evaluate to get actual register value */
val = mvpp2_usec_to_cycles(rxq->time_coal, freq);
}
/* Every RXQ has a pool for short and another for long packets */
err = xdp_rxq_info_reg_mem_model(&rxq->xdp_rxq_short,
MEM_TYPE_PAGE_POOL,
priv->page_pool[rxq->logic_rxq]); if (err < 0) goto err_unregister_rxq_long;
/* Push packets received by the RXQ to BM pool */ staticvoid mvpp2_rxq_drop_pkts(struct mvpp2_port *port, struct mvpp2_rx_queue *rxq)
{ int rx_received, i;
rx_received = mvpp2_rxq_received(port, rxq->id); if (!rx_received) return;
for (i = 0; i < rx_received; i++) { struct mvpp2_rx_desc *rx_desc = mvpp2_rxq_next_desc_get(rxq);
u32 status = mvpp2_rxdesc_status_get(port, rx_desc); int pool;
pool = (status & MVPP2_RXD_BM_POOL_ID_MASK) >>
MVPP2_RXD_BM_POOL_ID_OFFS;
/* Calculate base address in prefetch buffer. We reserve 16 descriptors * for each existing TXQ. * TCONTS for PON port must be continuous from 0 to MVPP2_MAX_TCONT * GBE ports assumed to be continuous from 0 to MVPP2_MAX_PORTS
*/
desc_per_txq = 16;
desc = (port->id * MVPP2_MAX_TXQ * desc_per_txq) +
(txq->log_id * desc_per_txq);
val = mvpp2_read(port->priv, MVPP2_TXQ_SCHED_REFILL_REG(txq->log_id));
val &= ~MVPP2_TXQ_REFILL_PERIOD_ALL_MASK;
val |= MVPP2_TXQ_REFILL_PERIOD_MASK(1);
val |= MVPP2_TXQ_REFILL_TOKENS_ALL_MASK;
mvpp2_write(port->priv, MVPP2_TXQ_SCHED_REFILL_REG(txq->log_id), val);
val = MVPP2_TXQ_TOKEN_SIZE_MAX;
mvpp2_write(port->priv, MVPP2_TXQ_SCHED_TOKEN_SIZE_REG(txq->log_id),
val);
mvpp2_thread_write(port->priv, thread, MVPP2_TXQ_NUM_REG, txq->id);
val = mvpp2_thread_read(port->priv, thread, MVPP2_TXQ_PREF_BUF_REG);
val |= MVPP2_TXQ_DRAIN_EN_MASK;
mvpp2_thread_write(port->priv, thread, MVPP2_TXQ_PREF_BUF_REG, val);
/* The napi queue has been stopped so wait for all packets * to be transmitted.
*/
delay = 0; do { if (delay >= MVPP2_TX_PENDING_TIMEOUT_MSEC) {
netdev_warn(port->dev, "port %d: cleaning queue %d timed out\n",
port->id, txq->log_id); break;
}
mdelay(1);
delay++;
if (phy_interface_mode_is_rgmii(port->phy_interface) ||
phy_interface_mode_is_8023z(port->phy_interface) ||
port->phy_interface == PHY_INTERFACE_MODE_SGMII) {
val = readl(port->base + MVPP22_GMAC_INT_STAT); if (val & MVPP22_GMAC_INT_STAT_LINK) {
val = readl(port->base + MVPP2_GMAC_STATUS0);
link = (val & MVPP2_GMAC_STATUS0_LINK_UP);
mvpp2_isr_handle_link(port, &port->pcs_gmac, link);
}
}
}
/* Per-port interrupt for link status changes */ static irqreturn_t mvpp2_port_isr(int irq, void *dev_id)
{ struct mvpp2_port *port = (struct mvpp2_port *)dev_id;
u32 val;
mvpp22_gop_mask_irq(port);
if (mvpp2_port_supports_xlg(port) &&
mvpp2_is_xlg(port->phy_interface)) { /* Check the external status register */
val = readl(port->base + MVPP22_XLG_EXT_INT_STAT); if (val & MVPP22_XLG_EXT_INT_STAT_XLG)
mvpp2_isr_handle_xlg(port); if (val & MVPP22_XLG_EXT_INT_STAT_PTP)
mvpp2_isr_handle_ptp(port);
} else { /* If it's not the XLG, we must be using the GMAC. * Check the summary status.
*/
val = readl(port->base + MVPP22_GMAC_INT_SUM_STAT); if (val & MVPP22_GMAC_INT_SUM_STAT_INTERNAL)
mvpp2_isr_handle_gmac_internal(port); if (val & MVPP22_GMAC_INT_SUM_STAT_PTP)
mvpp2_isr_handle_ptp(port);
}
port_pcpu = container_of(timer, struct mvpp2_port_pcpu, tx_done_timer);
dev = port_pcpu->dev;
if (!netif_running(dev)) return HRTIMER_NORESTART;
port_pcpu->timer_scheduled = false;
port = netdev_priv(dev);
/* Process all the Tx queues */
cause = (1 << port->ntxqs) - 1;
tx_todo = mvpp2_tx_done(port, cause,
mvpp2_cpu_to_thread(port->priv, smp_processor_id()));
/* Set the timer in case not all the packets were processed */ if (tx_todo && !port_pcpu->timer_scheduled) {
port_pcpu->timer_scheduled = true;
hrtimer_forward_now(&port_pcpu->tx_done_timer,
MVPP2_TXDONE_HRTIMER_PERIOD_NS);
/* Allocate a new skb and add it to BM pool */ staticint mvpp2_rx_refill(struct mvpp2_port *port, struct mvpp2_bm_pool *bm_pool, struct page_pool *page_pool, int pool)
{
dma_addr_t dma_addr;
phys_addr_t phys_addr; void *buf;
/* Check number of available descriptors */ if (mvpp2_aggr_desc_num_check(port, aggr_txq, 1) ||
mvpp2_txq_reserved_desc_num_proc(port, txq, txq_pcpu, 1)) {
ret = MVPP2_XDP_DROPPED; goto out;
}
/* Get a descriptor for the first part of the packet */
tx_desc = mvpp2_txq_next_desc_get(aggr_txq);
mvpp2_txdesc_txq_set(port, tx_desc, txq->id);
mvpp2_txdesc_size_set(port, tx_desc, xdpf->len);
if (dma_map) { /* XDP_REDIRECT or AF_XDP */
dma_addr = dma_map_single(port->dev->dev.parent, xdpf->data,
xdpf->len, DMA_TO_DEVICE);
if (unlikely(dma_mapping_error(port->dev->dev.parent, dma_addr))) {
mvpp2_txq_desc_put(txq);
ret = MVPP2_XDP_DROPPED; goto out;
}
xdpf = xdp_convert_buff_to_frame(xdp); if (unlikely(!xdpf)) return MVPP2_XDP_DROPPED;
/* The first of the TX queues are used for XPS, * the second half for XDP_TX
*/
txq_id = mvpp2_cpu_to_thread(port->priv, smp_processor_id()) + (port->ntxqs / 2);
ret = mvpp2_xdp_submit_frame(port, txq_id, xdpf, false); if (ret == MVPP2_XDP_TX) {
u64_stats_update_begin(&stats->syncp);
stats->tx_bytes += xdpf->len;
stats->tx_packets++;
stats->xdp_tx++;
u64_stats_update_end(&stats->syncp);
if (unlikely(test_bit(0, &port->state))) return -ENETDOWN;
if (unlikely(flags & ~XDP_XMIT_FLAGS_MASK)) return -EINVAL;
/* The first of the TX queues are used for XPS, * the second half for XDP_TX
*/
txq_id = mvpp2_cpu_to_thread(port->priv, smp_processor_id()) + (port->ntxqs / 2);
for (i = 0; i < num_frame; i++) {
ret = mvpp2_xdp_submit_frame(port, txq_id, frames[i], true); if (ret != MVPP2_XDP_TX) break;
nxmit_byte += frames[i]->len;
nxmit++;
}
if (likely(nxmit > 0))
mvpp2_xdp_finish_tx(port, txq_id, nxmit, nxmit_byte);
/* Get number of received packets and clamp the to-do */
rx_received = mvpp2_rxq_received(port, rxq->id); if (rx_todo > rx_received)
rx_todo = rx_received;
/* Buffer header not supported */ if (rx_status & MVPP2_RXD_BUF_HDR) goto err_drop_frame;
/* In case of an error, release the requested buffer pointer * to the Buffer Manager. This request process is controlled * by the hardware, and the information about the buffer is * comprised by the RX descriptor.
*/ if (rx_status & MVPP2_RXD_ERR_SUMMARY) goto err_drop_frame;
if (frag_size)
skb = build_skb(data, frag_size); else
skb = slab_build_skb(data); if (!skb) {
netdev_warn(port->dev, "skb build failed\n"); goto err_drop_frame;
}
/* If we have RX hardware timestamping enabled, grab the * timestamp from the queue and convert.
*/ if (mvpp22_rx_hwtstamping(port)) {
timestamp = le32_to_cpu(rx_desc->pp22.timestamp);
mvpp22_tai_tstamp(port->priv->tai, timestamp,
skb_hwtstamps(skb));
}
err = mvpp2_rx_refill(port, bm_pool, pp, pool); if (err) {
netdev_err(port->dev, "failed to refill BM pools\n");
dev_kfree_skb_any(skb); goto err_drop_frame;
}
if (pp)
skb_mark_for_recycle(skb); else
dma_unmap_single_attrs(dev->dev.parent, dma_addr,
bm_pool->buf_size, DMA_FROM_DEVICE,
DMA_ATTR_SKIP_CPU_SYNC);
err_drop_frame:
dev->stats.rx_errors++;
mvpp2_rx_error(port, rx_desc); /* Return the buffer to the pool */ if (rx_status & MVPP2_RXD_BUF_HDR)
mvpp2_buff_hdr_pool_put(port, rx_desc, pool, rx_status); else
mvpp2_bm_pool_put(port, pool, dma_addr, phys_addr);
}
if (xdp_ret & MVPP2_XDP_REDIR)
xdp_do_flush();
if (ps.rx_packets) { struct mvpp2_pcpu_stats *stats = this_cpu_ptr(port->stats);
staticvoid mvpp2_txdesc_clear_ptp(struct mvpp2_port *port, struct mvpp2_tx_desc *desc)
{ /* We only need to clear the low bits */ if (port->priv->hw_version >= MVPP22)
desc->pp22.ptp_descriptor &=
cpu_to_le32(~MVPP22_PTP_DESC_MASK_LOW);
}
case PTP_CLASS_V2:
ptpdesc |= MVPP22_PTP_PACKETFORMAT(MVPP22_PTP_PKT_FMT_PTPV2);
mtype = hdr->tsmt & 15; /* Direct PTP Sync messages to queue 1 */ if (mtype == 0) {
ptpdesc |= MVPP22_PTP_TIMESTAMPQUEUESELECT;
queue = &port->tx_hwtstamp_queue[1];
} break;
}
/* Take a reference on the skb and insert into our queue */
i = queue->next;
queue->next = (i + 1) & 31; if (queue->skb[i])
dev_kfree_skb_any(queue->skb[i]);
queue->skb[i] = skb_get(skb);
if (i == (skb_shinfo(skb)->nr_frags - 1)) { /* Last descriptor */
mvpp2_txdesc_cmd_set(port, tx_desc,
MVPP2_TXD_L_DESC);
mvpp2_txq_inc_put(port, txq_pcpu, skb, tx_desc, MVPP2_TYPE_SKB);
} else { /* Descriptor in the middle: Not First, Not Last */
mvpp2_txdesc_cmd_set(port, tx_desc, 0);
mvpp2_txq_inc_put(port, txq_pcpu, NULL, tx_desc, MVPP2_TYPE_SKB);
}
}
return 0;
cleanup: /* Release all descriptors that were used to map fragments of * this packet, as well as the corresponding DMA mappings
*/ for (i = i - 1; i >= 0; i--) {
tx_desc = txq->descs + i;
tx_desc_unmap_put(port, txq, tx_desc);
}
/* Check number of available descriptors */ if (mvpp2_aggr_desc_num_check(port, aggr_txq, tso_count_descs(skb)) ||
mvpp2_txq_reserved_desc_num_proc(port, txq, txq_pcpu,
tso_count_descs(skb))) return 0;
hdr_sz = tso_start(skb, &tso);
len = skb->len - hdr_sz; while (len > 0) { int left = min_t(int, skb_shinfo(skb)->gso_size, len); char *hdr = txq_pcpu->tso_headers +
txq_pcpu->txq_put_index * TSO_HEADER_SIZE;
/* Check number of available descriptors */ if (mvpp2_aggr_desc_num_check(port, aggr_txq, frags) ||
mvpp2_txq_reserved_desc_num_proc(port, txq, txq_pcpu, frags)) {
frags = 0; goto out;
}
/* Get a descriptor for the first part of the packet */
tx_desc = mvpp2_txq_next_desc_get(aggr_txq); if (!(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) ||
!mvpp2_tx_hw_tstamp(port, tx_desc, skb))
mvpp2_txdesc_clear_ptp(port, tx_desc);
mvpp2_txdesc_txq_set(port, tx_desc, txq->id);
mvpp2_txdesc_size_set(port, tx_desc, skb_headlen(skb));
/* Set the timer in case not all frags were processed */ if (!port->has_tx_irqs && txq_pcpu->count <= frags &&
txq_pcpu->count > 0) { struct mvpp2_port_pcpu *port_pcpu = per_cpu_ptr(port->pcpu, thread);
/* Rx/Tx cause register * * Bits 0-15: each bit indicates received packets on the Rx queue * (bit 0 is for Rx queue 0). * * Bits 16-23: each bit indicates transmitted packets on the Tx queue * (bit 16 is for Tx queue 0). * * Each CPU has its own Rx/Tx cause register
*/
cause_rx_tx = mvpp2_thread_read_relaxed(port->priv, qv->sw_thread_id,
MVPP2_ISR_RX_TX_CAUSE_REG(port->id));
cause_misc = cause_rx_tx & MVPP2_CAUSE_MISC_SUM_MASK; if (cause_misc) {
mvpp2_cause_error(port->dev, cause_misc);
/* Clear the cause register */
mvpp2_write(port->priv, MVPP2_ISR_MISC_CAUSE_REG, 0);
mvpp2_thread_write(port->priv, thread,
MVPP2_ISR_RX_TX_CAUSE_REG(port->id),
cause_rx_tx & ~MVPP2_CAUSE_MISC_SUM_MASK);
}
if (port->has_tx_irqs) {
cause_tx = cause_rx_tx & MVPP2_CAUSE_TXQ_OCCUP_DESC_ALL_MASK; if (cause_tx) {
cause_tx >>= MVPP2_CAUSE_TXQ_OCCUP_DESC_ALL_OFFSET;
mvpp2_tx_done(port, cause_tx, qv->sw_thread_id);
}
}
/* Process RX packets */
cause_rx = cause_rx_tx &
MVPP2_CAUSE_RXQ_OCCUP_DESC_ALL_MASK(port->priv->hw_version);
cause_rx <<= qv->first_rxq;
cause_rx |= qv->pending_cause_rx; while (cause_rx && budget > 0) { int count; struct mvpp2_rx_queue *rxq;
rxq = mvpp2_get_rx_queue(port, cause_rx); if (!rxq) break;
count = mvpp2_rx(port, napi, budget, rxq);
rx_done += count;
budget -= count; if (budget > 0) { /* Clear the bit associated to this Rx queue * so that next iteration will continue from * the next Rx queue.
*/
cause_rx &= ~(1 << rxq->logic_rxq);
}
}
if (budget > 0) {
cause_rx = 0;
napi_complete_done(napi, rx_done);
/* The Tx ring size cannot be smaller than the minimum number of * descriptors needed for TSO.
*/ if (new_tx_pending < MVPP2_MAX_SKB_DESCS)
new_tx_pending = ALIGN(MVPP2_MAX_SKB_DESCS, 32);
if (ring->rx_pending != new_rx_pending) {
netdev_info(dev, "illegal Rx ring size value %d, round to %d\n",
ring->rx_pending, new_rx_pending);
ring->rx_pending = new_rx_pending;
}
if (ring->tx_pending != new_tx_pending) {
netdev_info(dev, "illegal Tx ring size value %d, round to %d\n",
ring->tx_pending, new_tx_pending);
ring->tx_pending = new_tx_pending;
}
if (!is_valid_ether_addr(addr->sa_data)) return -EADDRNOTAVAIL;
err = mvpp2_prs_update_mac_da(dev, addr->sa_data); if (err) { /* Reconfigure parser accept the original MAC address */
mvpp2_prs_update_mac_da(dev, dev->dev_addr);
netdev_err(dev, "failed to change MAC address\n");
} return err;
}
/* Shut down all the ports, reconfigure the pools as percpu or shared, * then bring up again all ports.
*/ staticint mvpp2_bm_switch_buffers(struct mvpp2 *priv, bool percpu)
{ bool change_percpu = (percpu != priv->percpu_pools); int numbufs = MVPP2_BM_POOLS_NUM, i; struct mvpp2_port *port = NULL; bool status[MVPP2_MAX_PORTS];
for (i = 0; i < priv->port_count; i++) {
port = priv->port_list[i];
status[i] = netif_running(port->dev); if (status[i])
mvpp2_stop(port->dev);
}
/* nrxqs is the same for all ports */ if (priv->percpu_pools)
numbufs = port->nrxqs * 2;
if (change_percpu)
mvpp2_bm_pool_update_priv_fc(priv, false);
for (i = 0; i < numbufs; i++)
mvpp2_bm_pool_destroy(port->dev->dev.parent, priv, &priv->bm_pools[i]);
if (!IS_ALIGNED(MVPP2_RX_PKT_SIZE(mtu), 8)) {
netdev_info(dev, "illegal MTU value %d, round to %d\n", mtu,
ALIGN(MVPP2_RX_PKT_SIZE(mtu), 8));
mtu = ALIGN(MVPP2_RX_PKT_SIZE(mtu), 8);
}
if (port->xdp_prog && mtu > MVPP2_MAX_RX_BUF_SIZE) {
netdev_err(dev, "Illegal MTU value %d (> %d) for XDP mode\n",
mtu, (int)MVPP2_MAX_RX_BUF_SIZE); return -EINVAL;
}
if (MVPP2_RX_PKT_SIZE(mtu) > MVPP2_BM_LONG_PKT_SIZE) { if (priv->percpu_pools) {
netdev_warn(dev, "mtu %d too high, switching to shared buffers", mtu);
mvpp2_bm_switch_buffers(priv, false);
}
} else { bool jumbo = false; int i;
for (i = 0; i < priv->port_count; i++) if (priv->port_list[i] != port &&
MVPP2_RX_PKT_SIZE(priv->port_list[i]->dev->mtu) >
MVPP2_BM_LONG_PKT_SIZE) {
jumbo = true; break;
}
/* No port is using jumbo frames */ if (!jumbo) {
dev_info(port->dev->dev.parent, "all ports have a low MTU, switching to per-cpu buffers");
mvpp2_bm_switch_buffers(priv, true);
}
}
if (running)
mvpp2_stop_dev(port);
err = mvpp2_bm_update_mtu(dev, mtu); if (err) {
netdev_err(dev, "failed to change MTU\n"); /* Reconfigure BM to the original MTU */
mvpp2_bm_update_mtu(dev, dev->mtu);
} else {
port->pkt_size = MVPP2_RX_PKT_SIZE(mtu);
}
if (running) {
mvpp2_start_dev(port);
mvpp2_egress_enable(port);
mvpp2_ingress_enable(port);
}
/* It seems we must also release the TX reset when enabling the TSU */ if (config->rx_filter != HWTSTAMP_FILTER_NONE)
gcr |= MVPP22_PTP_GCR_TSU_ENABLE | MVPP22_PTP_GCR_RX_RESET |
MVPP22_PTP_GCR_TX_RESET;
if (gcr & MVPP22_PTP_GCR_TSU_ENABLE)
mvpp22_tai_start(port->priv->tai);
staticint mvpp2_vlan_rx_add_vid(struct net_device *dev, __be16 proto, u16 vid)
{ struct mvpp2_port *port = netdev_priv(dev); int ret;
ret = mvpp2_prs_vid_entry_add(port, vid); if (ret)
netdev_err(dev, "rx-vlan-filter offloading cannot accept more than %d VIDs per port\n",
MVPP2_PRS_VLAN_FILT_MAX - 1); return ret;
}
if (changed & NETIF_F_HW_VLAN_CTAG_FILTER) { if (features & NETIF_F_HW_VLAN_CTAG_FILTER) {
mvpp2_prs_vid_enable_filtering(port);
} else { /* Invalidate all registered VID filters for this * port
*/
mvpp2_prs_vid_remove_all(port);
mvpp2_prs_vid_disable_filtering(port);
}
}
if (changed & NETIF_F_RXHASH) { if (features & NETIF_F_RXHASH)
mvpp22_port_rss_enable(port); else
mvpp22_port_rss_disable(port);
}
/* The interface is running, so we have to force a * reallocation of the queues
*/
mvpp2_stop_dev(port);
mvpp2_cleanup_rxqs(port);
mvpp2_cleanup_txqs(port);
err = mvpp2_setup_rxqs(port); if (err) { /* Reallocate Rx queues with the original ring size */
port->rx_ring_size = prev_rx_ring_size;
ring->rx_pending = prev_rx_ring_size;
err = mvpp2_setup_rxqs(port); if (err) goto err_out;
}
err = mvpp2_setup_txqs(port); if (err) { /* Reallocate Tx queues with the original ring size */
port->tx_ring_size = prev_tx_ring_size;
ring->tx_pending = prev_tx_ring_size;
err = mvpp2_setup_txqs(port); if (err) goto err_clean_rxqs;
}
staticint mvpp2_ethtool_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *info, u32 *rules)
{ struct mvpp2_port *port = netdev_priv(dev); int ret = 0, i, loc = 0;
if (!mvpp22_rss_is_supported(port)) return -EOPNOTSUPP;
switch (info->cmd) { case ETHTOOL_GRXRINGS:
info->data = port->nrxqs; break; case ETHTOOL_GRXCLSRLCNT:
info->rule_cnt = port->n_rfs_rules; break; case ETHTOOL_GRXCLSRULE:
ret = mvpp2_ethtool_cls_rule_get(port, info); break; case ETHTOOL_GRXCLSRLALL: for (i = 0; i < MVPP2_N_RFS_ENTRIES_PER_FLOW; i++) { if (loc == info->rule_cnt) {
ret = -EMSGSIZE; break;
}
/* Used for PPv2.1, or PPv2.2 with the old Device Tree binding that * had a single IRQ defined per-port.
*/ staticint mvpp2_simple_queue_vectors_init(struct mvpp2_port *port, struct device_node *port_node)
{ struct mvpp2_queue_vector *v = &port->qvecs[0];
staticvoid mvpp2_queue_vectors_deinit(struct mvpp2_port *port)
{ int i;
for (i = 0; i < port->nqvecs; i++)
irq_dispose_mapping(port->qvecs[i].irq);
}
/* Configure Rx queue group interrupt for this port */ staticvoid mvpp2_rx_irqs_setup(struct mvpp2_port *port)
{ struct mvpp2 *priv = port->priv;
u32 val; int i;
if (priv->hw_version == MVPP21) {
mvpp2_write(priv, MVPP21_ISR_RXQ_GROUP_REG(port->id),
port->nrxqs); return;
}
/* Handle the more complicated PPv2.2 and PPv2.3 case */ for (i = 0; i < port->nqvecs; i++) { struct mvpp2_queue_vector *qv = port->qvecs + i;
if (!qv->nrxqs) continue;
val = qv->sw_thread_id;
val |= port->id << MVPP22_ISR_RXQ_GROUP_INDEX_GROUP_OFFSET;
mvpp2_write(priv, MVPP22_ISR_RXQ_GROUP_INDEX_REG, val);
val = qv->first_rxq;
val |= qv->nrxqs << MVPP22_ISR_RXQ_SUB_GROUP_SIZE_OFFSET;
mvpp2_write(priv, MVPP22_ISR_RXQ_SUB_GROUP_CONFIG_REG, val);
}
}
/* Checks for hardware constraints */ if (port->first_rxq + port->nrxqs >
MVPP2_MAX_PORTS * priv->max_port_rxqs) return -EINVAL;
if (port->nrxqs > priv->max_port_rxqs || port->ntxqs > MVPP2_MAX_TXQ) return -EINVAL;
/* Disable port */
mvpp2_egress_disable(port);
mvpp2_port_disable(port);
if (mvpp2_is_xlg(port->phy_interface)) {
val = readl(port->base + MVPP22_XLG_CTRL0_REG);
val &= ~MVPP22_XLG_CTRL0_FORCE_LINK_PASS;
val |= MVPP22_XLG_CTRL0_FORCE_LINK_DOWN;
writel(val, port->base + MVPP22_XLG_CTRL0_REG);
} else {
val = readl(port->base + MVPP2_GMAC_AUTONEG_CONFIG);
val &= ~MVPP2_GMAC_FORCE_LINK_PASS;
val |= MVPP2_GMAC_FORCE_LINK_DOWN;
writel(val, port->base + MVPP2_GMAC_AUTONEG_CONFIG);
}
port->tx_time_coal = MVPP2_TXDONE_COAL_USEC;
port->txqs = devm_kcalloc(dev, port->ntxqs, sizeof(*port->txqs),
GFP_KERNEL); if (!port->txqs) return -ENOMEM;
/* Associate physical Tx queues to this port and initialize. * The mapping is predefined.
*/ for (queue = 0; queue < port->ntxqs; queue++) { int queue_phy_id = mvpp2_txq_phys(port->id, queue); struct mvpp2_tx_queue *txq;
for (i = 0; i < 5; i++) if (of_property_match_string(port_node, "interrupt-names",
irqs[i]) < 0) returnfalse;
*flags |= MVPP2_F_DT_COMPAT; returntrue;
}
/* Checks if the port dt description has the required Tx interrupts: * - PPv2.1: there are no such interrupts. * - PPv2.2 and PPv2.3: * - The old DTs have: "rx-shared", "tx-cpuX" with X in [0...3] * - The new ones have: "hifX" with X in [0..8] * * All those variants are supported to keep the backward compatibility.
*/ staticbool mvpp2_port_has_irqs(struct mvpp2 *priv, struct device_node *port_node, unsignedlong *flags)
{ char name[5]; int i;
/* ACPI */ if (!port_node) returntrue;
if (priv->hw_version == MVPP21) returnfalse;
if (mvpp22_port_has_legacy_tx_irqs(port_node, flags)) returntrue;
for (i = 0; i < MVPP2_MAX_THREADS; i++) {
snprintf(name, 5, "hif%d", i); if (of_property_match_string(port_node, "interrupt-names",
name) < 0) returnfalse;
}
if (priv->hw_version == MVPP21) {
mvpp21_get_mac_address(port, hw_mac_addr); if (is_valid_ether_addr(hw_mac_addr)) {
*mac_from = "hardware";
eth_hw_addr_set(dev, hw_mac_addr); return 0;
}
}
/* Only valid on OF enabled platforms */
ret = of_get_mac_address_nvmem(to_of_node(fwnode), fw_mac_addr); if (ret == -EPROBE_DEFER) return ret; if (!ret) {
*mac_from = "nvmem cell";
eth_hw_addr_set(dev, fw_mac_addr); return 0;
}
staticunsignedint mvpp2_gmac_pcs_inband_caps(struct phylink_pcs *pcs,
phy_interface_t interface)
{ /* When operating in an 802.3z mode, we must have AN enabled: * Bit 2 Field InBandAnEn In-band Auto-Negotiation enable. ... * When <PortType> = 1 (1000BASE-X) this field must be set to 1. * Therefore, inband is "required".
*/ if (phy_interface_mode_is_8023z(interface)) return LINK_INBAND_ENABLE;
/* SGMII and RGMII can be configured to use inband signalling of the * AN result. Indicate these as "possible".
*/ if (interface == PHY_INTERFACE_MODE_SGMII ||
phy_interface_mode_is_rgmii(interface)) return LINK_INBAND_DISABLE | LINK_INBAND_ENABLE;
/* For any other modes, indicate that inband is not supported. */ return LINK_INBAND_DISABLE;
}
if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) {
mask |= MVPP2_GMAC_CONFIG_MII_SPEED |
MVPP2_GMAC_CONFIG_GMII_SPEED |
MVPP2_GMAC_CONFIG_FULL_DUPLEX;
val = MVPP2_GMAC_IN_BAND_AUTONEG;
if (interface == PHY_INTERFACE_MODE_SGMII) { /* SGMII mode receives the speed and duplex from PHY */
val |= MVPP2_GMAC_AN_SPEED_EN |
MVPP2_GMAC_AN_DUPLEX_EN;
} else { /* 802.3z mode has fixed speed and duplex */
val |= MVPP2_GMAC_CONFIG_GMII_SPEED |
MVPP2_GMAC_CONFIG_FULL_DUPLEX;
/* The FLOW_CTRL_AUTONEG bit selects either the hardware * automatically or the bits in MVPP22_GMAC_CTRL_4_REG * manually controls the GMAC pause modes.
*/ if (permit_pause_to_mac)
val |= MVPP2_GMAC_FLOW_CTRL_AUTONEG;
/* Configure advertisement bits */
mask |= MVPP2_GMAC_FC_ADV_EN | MVPP2_GMAC_FC_ADV_ASM_EN; if (phylink_test(advertising, Pause))
val |= MVPP2_GMAC_FC_ADV_EN; if (phylink_test(advertising, Asym_Pause))
val |= MVPP2_GMAC_FC_ADV_ASM_EN;
}
} else {
val = 0;
}
old_an = an = readl(port->base + MVPP2_GMAC_AUTONEG_CONFIG);
an = (an & ~mask) | val;
changed = an ^ old_an; if (changed)
writel(an, port->base + MVPP2_GMAC_AUTONEG_CONFIG);
/* We are only interested in the advertisement bits changing */ return changed & (MVPP2_GMAC_FC_ADV_EN | MVPP2_GMAC_FC_ADV_ASM_EN);
}
/* Configure negotiation style */ if (!phylink_autoneg_inband(mode)) { /* Phy or fixed speed - no in-band AN, nothing to do, leave the * configured speed, duplex and flow control as-is.
*/
} elseif (state->interface == PHY_INTERFACE_MODE_SGMII) { /* SGMII in-band mode receives the speed and duplex from
* the PHY. Flow control information is not received. */
} elseif (phy_interface_mode_is_8023z(state->interface)) { /* 1000BaseX and 2500BaseX ports cannot negotiate speed nor can * they negotiate duplex: they are always operating with a fixed * speed of 1000/2500Mbps in full duplex, so force 1000/2500 * speed and full duplex here.
*/
ctrl0 |= MVPP2_GMAC_PORT_TYPE_MASK;
}
if (old_ctrl0 != ctrl0)
writel(ctrl0, port->base + MVPP2_GMAC_CTRL_0_REG); if (old_ctrl2 != ctrl2)
writel(ctrl2, port->base + MVPP2_GMAC_CTRL_2_REG); if (old_ctrl4 != ctrl4)
writel(ctrl4, port->base + MVPP22_GMAC_CTRL_4_REG);
}
/* Select the appropriate PCS operations depending on the * configured interface mode. We will only switch to a mode * that the validate() checks have already passed.
*/ if (mvpp2_is_xlg(interface)) return &port->pcs_xlg; else return &port->pcs_gmac;
}
/* Check for invalid configuration */ if (mvpp2_is_xlg(interface) && port->gop_id != 0) {
netdev_err(port->dev, "Invalid mode on %s\n", port->dev->name); return -EINVAL;
}
if (port->phy_interface != interface ||
phylink_autoneg_inband(mode)) { /* Force the link down when changing the interface or if in * in-band mode to ensure we do not change the configuration * while the hardware is indicating link is up. We force both * XLG and GMAC down to ensure that they're both in a known * state.
*/
mvpp2_modify(port->base + MVPP2_GMAC_AUTONEG_CONFIG,
MVPP2_GMAC_FORCE_LINK_PASS |
MVPP2_GMAC_FORCE_LINK_DOWN,
MVPP2_GMAC_FORCE_LINK_DOWN);
if (mvpp2_port_supports_xlg(port))
mvpp2_modify(port->base + MVPP22_XLG_CTRL0_REG,
MVPP22_XLG_CTRL0_FORCE_LINK_PASS |
MVPP22_XLG_CTRL0_FORCE_LINK_DOWN,
MVPP22_XLG_CTRL0_FORCE_LINK_DOWN);
}
/* Make sure the port is disabled when reconfiguring the mode */
mvpp2_port_disable(port);
if (port->phy_interface != interface) { /* Place GMAC into reset */
mvpp2_modify(port->base + MVPP2_GMAC_CTRL_2_REG,
MVPP2_GMAC_PORT_RESET_MASK,
MVPP2_GMAC_PORT_RESET_MASK);
if (port->priv->hw_version >= MVPP22) {
mvpp22_gop_mask_irq(port);
phy_power_off(port->comphy);
/* Reconfigure the serdes lanes */
mvpp22_mode_reconfigure(port, interface);
}
}
if (!mvpp2_is_xlg(interface)) { /* Release GMAC reset and wait */
mvpp2_modify(port->base + MVPP2_GMAC_CTRL_2_REG,
MVPP2_GMAC_PORT_RESET_MASK, 0);
while (readl(port->base + MVPP2_GMAC_CTRL_2_REG) &
MVPP2_GMAC_PORT_RESET_MASK) continue;
}
mvpp2_port_enable(port);
/* Allow the link to come up if in in-band mode, otherwise the * link is forced via mac_link_down()/mac_link_up()
*/ if (phylink_autoneg_inband(mode)) { if (mvpp2_is_xlg(interface))
mvpp2_modify(port->base + MVPP22_XLG_CTRL0_REG,
MVPP22_XLG_CTRL0_FORCE_LINK_PASS |
MVPP22_XLG_CTRL0_FORCE_LINK_DOWN, 0); else
mvpp2_modify(port->base + MVPP2_GMAC_AUTONEG_CONFIG,
MVPP2_GMAC_FORCE_LINK_PASS |
MVPP2_GMAC_FORCE_LINK_DOWN, 0);
}
return 0;
}
staticvoid mvpp2_mac_link_up(struct phylink_config *config, struct phy_device *phy, unsignedint mode, phy_interface_t interface, int speed, int duplex, bool tx_pause, bool rx_pause)
{ struct mvpp2_port *port = mvpp2_phylink_to_port(config);
u32 val; int i;
if (mvpp2_is_xlg(interface)) { if (!phylink_autoneg_inband(mode)) {
val = MVPP22_XLG_CTRL0_FORCE_LINK_PASS; if (tx_pause)
val |= MVPP22_XLG_CTRL0_TX_FLOW_CTRL_EN; if (rx_pause)
val |= MVPP22_XLG_CTRL0_RX_FLOW_CTRL_EN;
/* We can always update the flow control enable bits; * these will only be effective if flow control AN * (MVPP2_GMAC_FLOW_CTRL_AUTONEG) is disabled.
*/
val = 0; if (tx_pause)
val |= MVPP22_CTRL4_TX_FC_EN; if (rx_pause)
val |= MVPP22_CTRL4_RX_FC_EN;
status = readl(port->base + MVPP2_GMAC_STATUS0); if (status & MVPP2_GMAC_STATUS0_GMII_SPEED) { /* At 1G speeds, the timer resolution are 1us, and * 802.3 says tw is 16.5us. Round up to 17us.
*/
tw = 17;
ts = timer;
} else { /* At 100M speeds, the timer resolutions are 10us, and * 802.3 says tw is 30us.
*/
tw = 3;
ts = DIV_ROUND_UP(timer, 10);
}
/* Work-around for ACPI */ staticvoid mvpp2_acpi_start(struct mvpp2_port *port)
{ /* Phylink isn't used as of now for ACPI, so the MAC has to be * configured manually when the interface is started. This will * be removed as soon as the phylink ACPI support lands in.
*/ struct phylink_link_state state = {
.interface = port->phy_interface,
}; struct phylink_pcs *pcs;
/* In order to ensure backward compatibility for ACPI, check if the port * firmware node comprises the necessary description allowing to use phylink.
*/ staticbool mvpp2_use_acpi_compat_mode(struct fwnode_handle *port_fwnode)
{ if (!is_acpi_node(port_fwnode)) returnfalse;
/* * Rewrite 10GBASE-KR to 10GBASE-R for compatibility with existing DT. * Existing usage of 10GBASE-KR is not correct; no backplane * negotiation is done, and this driver does not actually support * 10GBASE-KR.
*/ if (phy_mode == PHY_INTERFACE_MODE_10GKR)
phy_mode = PHY_INTERFACE_MODE_10GBASER;
if (port_node) {
comphy = devm_of_phy_get(&pdev->dev, port_node, NULL); if (IS_ERR(comphy)) { if (PTR_ERR(comphy) == -EPROBE_DEFER) {
err = -EPROBE_DEFER; goto err_free_netdev;
}
comphy = NULL;
}
}
if (port->priv->global_tx_fc)
port->phylink_config.mac_capabilities |=
MAC_SYM_PAUSE | MAC_ASYM_PAUSE;
if (mvpp2_port_supports_xlg(port)) { /* If a COMPHY is present, we can support any of * the serdes modes and switch between them.
*/ if (comphy) {
__set_bit(PHY_INTERFACE_MODE_5GBASER,
port->phylink_config.supported_interfaces);
__set_bit(PHY_INTERFACE_MODE_10GBASER,
port->phylink_config.supported_interfaces);
__set_bit(PHY_INTERFACE_MODE_XAUI,
port->phylink_config.supported_interfaces);
} elseif (phy_mode == PHY_INTERFACE_MODE_5GBASER) {
__set_bit(PHY_INTERFACE_MODE_5GBASER,
port->phylink_config.supported_interfaces);
} elseif (phy_mode == PHY_INTERFACE_MODE_10GBASER) {
__set_bit(PHY_INTERFACE_MODE_10GBASER,
port->phylink_config.supported_interfaces);
} elseif (phy_mode == PHY_INTERFACE_MODE_XAUI) {
__set_bit(PHY_INTERFACE_MODE_XAUI,
port->phylink_config.supported_interfaces);
}
if (mvpp2_port_supports_rgmii(port)) {
phy_interface_set_rgmii(port->phylink_config.supported_interfaces);
__set_bit(PHY_INTERFACE_MODE_MII,
port->phylink_config.supported_interfaces);
}
if (comphy) { /* If a COMPHY is present, we can support any of the * serdes modes and switch between them.
*/
__set_bit(PHY_INTERFACE_MODE_SGMII,
port->phylink_config.supported_interfaces);
__set_bit(PHY_INTERFACE_MODE_1000BASEX,
port->phylink_config.supported_interfaces);
__set_bit(PHY_INTERFACE_MODE_2500BASEX,
port->phylink_config.supported_interfaces);
} elseif (phy_mode == PHY_INTERFACE_MODE_2500BASEX) { /* No COMPHY, with only 2500BASE-X mode supported */
__set_bit(PHY_INTERFACE_MODE_2500BASEX,
port->phylink_config.supported_interfaces);
} elseif (phy_mode == PHY_INTERFACE_MODE_1000BASEX ||
phy_mode == PHY_INTERFACE_MODE_SGMII) { /* No COMPHY, we can switch between 1000BASE-X and SGMII
*/
__set_bit(PHY_INTERFACE_MODE_1000BASEX,
port->phylink_config.supported_interfaces);
__set_bit(PHY_INTERFACE_MODE_SGMII,
port->phylink_config.supported_interfaces);
}
mvpp2_mac_disable_tx_lpi(&port->phylink_config);
} else {
dev_warn(&pdev->dev, "Use link irqs for port#%d. FW update required\n", port->id);
port->phylink = NULL;
}
/* Cycle the comphy to power it down, saving 270mW per port - * don't worry about an error powering it up. When the comphy * driver does this, we can remove this code.
*/ if (port->comphy) {
err = mvpp22_comphy_init(port, port->phy_interface); if (err == 0)
phy_power_off(port->comphy);
}
err = register_netdev(dev); if (err < 0) {
dev_err(&pdev->dev, "failed to register netdev\n"); goto err_phylink;
}
netdev_info(dev, "Using %s mac address %pM\n", mac_from, dev->dev_addr);
priv->port_list[priv->port_count++] = port;
return 0;
err_phylink: if (port->phylink)
phylink_destroy(port->phylink);
err_free_port_pcpu:
free_percpu(port->pcpu);
err_free_txq_pcpu: for (i = 0; i < port->ntxqs; i++)
free_percpu(port->txqs[i]->pcpu);
err_free_stats:
free_percpu(port->stats);
err_free_irq: if (port->port_irq)
irq_dispose_mapping(port->port_irq);
err_deinit_qvecs:
mvpp2_queue_vectors_deinit(port);
err_free_netdev:
free_netdev(dev); return err;
}
unregister_netdev(port->dev); if (port->phylink)
phylink_destroy(port->phylink);
free_percpu(port->pcpu);
free_percpu(port->stats); for (i = 0; i < port->ntxqs; i++)
free_percpu(port->txqs[i]->pcpu);
mvpp2_queue_vectors_deinit(port); if (port->port_irq)
irq_dispose_mapping(port->port_irq);
free_netdev(port->dev);
}
/* Initialize decoding windows */ staticvoid mvpp2_conf_mbus_windows(conststruct mbus_dram_target_info *dram, struct mvpp2 *priv)
{
u32 win_enable; int i;
for (i = 0; i < 6; i++) {
mvpp2_write(priv, MVPP2_WIN_BASE(i), 0);
mvpp2_write(priv, MVPP2_WIN_SIZE(i), 0);
if (i < 4)
mvpp2_write(priv, MVPP2_WIN_REMAP(i), 0);
}
win_enable = 0;
for (i = 0; i < dram->num_cs; i++) { conststruct mbus_dram_window *cs = dram->cs + i;
/* Initialize TX FIFO's: the total FIFO size is 48kB on PPv2.2 and PPv2.3. * 4kB fixed space must be assigned for the loopback port. * Redistribute remaining avialable 44kB space among all active ports. * Guarantee minimum 32kB for 10G port and 8kB for port 1, capable of 2.5G * SGMII link.
*/ staticvoid mvpp22_rx_fifo_init(struct mvpp2 *priv)
{ int remaining_ports_count; unsignedlong port_map; int size_remainder; int port, size;
/* The loopback requires fixed 4kB of the FIFO space assignment. */
mvpp22_rx_fifo_set_hw(priv, MVPP2_LOOPBACK_PORT_INDEX,
MVPP2_RX_FIFO_PORT_DATA_SIZE_4KB);
port_map = priv->port_map & ~BIT(MVPP2_LOOPBACK_PORT_INDEX);
/* Set RX FIFO size to 0 for inactive ports. */
for_each_clear_bit(port, &port_map, MVPP2_LOOPBACK_PORT_INDEX)
mvpp22_rx_fifo_set_hw(priv, port, 0);
/* Assign remaining RX FIFO space among all active ports. */
size_remainder = MVPP2_RX_FIFO_PORT_DATA_SIZE_44KB;
remaining_ports_count = hweight_long(port_map);
/* Configure Rx FIFO Flow control thresholds */ staticvoid mvpp23_rx_fifo_fc_set_tresh(struct mvpp2 *priv)
{ int port, val;
/* Port 0: maximum speed -10Gb/s port * required by spec RX FIFO threshold 9KB * Port 1: maximum speed -5Gb/s port * required by spec RX FIFO threshold 4KB * Port 2: maximum speed -1Gb/s port * required by spec RX FIFO threshold 2KB
*/
/* Without loopback port */ for (port = 0; port < (MVPP2_MAX_PORTS - 1); port++) { if (port == 0) {
val = (MVPP23_PORT0_FIFO_TRSH / MVPP2_RX_FC_TRSH_UNIT)
<< MVPP2_RX_FC_TRSH_OFFS;
val &= MVPP2_RX_FC_TRSH_MASK;
mvpp2_write(priv, MVPP2_RX_FC_REG(port), val);
} elseif (port == 1) {
val = (MVPP23_PORT1_FIFO_TRSH / MVPP2_RX_FC_TRSH_UNIT)
<< MVPP2_RX_FC_TRSH_OFFS;
val &= MVPP2_RX_FC_TRSH_MASK;
mvpp2_write(priv, MVPP2_RX_FC_REG(port), val);
} else {
val = (MVPP23_PORT2_FIFO_TRSH / MVPP2_RX_FC_TRSH_UNIT)
<< MVPP2_RX_FC_TRSH_OFFS;
val &= MVPP2_RX_FC_TRSH_MASK;
mvpp2_write(priv, MVPP2_RX_FC_REG(port), val);
}
}
}
/* Configure Rx FIFO Flow control thresholds */ void mvpp23_rx_fifo_fc_en(struct mvpp2 *priv, int port, bool en)
{ int val;
val = mvpp2_read(priv, MVPP2_RX_FC_REG(port));
if (en)
val |= MVPP2_RX_FC_EN; else
val &= ~MVPP2_RX_FC_EN;
mvpp2_write(priv, MVPP2_RX_FC_REG(port), val);
}
staticvoid mvpp22_tx_fifo_set_hw(struct mvpp2 *priv, int port, int size)
{ int threshold = MVPP2_TX_FIFO_THRESHOLD(size);
/* Initialize TX FIFO's: the total FIFO size is 19kB on PPv2.2 and PPv2.3. * 1kB fixed space must be assigned for the loopback port. * Redistribute remaining avialable 18kB space among all active ports. * The 10G interface should use 10kB (which is maximum possible size * per single port).
*/ staticvoid mvpp22_tx_fifo_init(struct mvpp2 *priv)
{ int remaining_ports_count; unsignedlong port_map; int size_remainder; int port, size;
/* The loopback requires fixed 1kB of the FIFO space assignment. */
mvpp22_tx_fifo_set_hw(priv, MVPP2_LOOPBACK_PORT_INDEX,
MVPP22_TX_FIFO_DATA_SIZE_1KB);
port_map = priv->port_map & ~BIT(MVPP2_LOOPBACK_PORT_INDEX);
/* Set TX FIFO size to 0 for inactive ports. */
for_each_clear_bit(port, &port_map, MVPP2_LOOPBACK_PORT_INDEX)
mvpp22_tx_fifo_set_hw(priv, port, 0);
/* Assign remaining TX FIFO space among all active ports. */
size_remainder = MVPP22_TX_FIFO_DATA_SIZE_18KB;
remaining_ports_count = hweight_long(port_map);
res = platform_get_resource(pdev, IORESOURCE_MEM, 2); if (!res) { if (has_acpi_companion(&pdev->dev))
dev_warn(&pdev->dev, "ACPI is too old, Flow control not supported\n"); else
dev_warn(&pdev->dev, "DT is too old, Flow control not supported\n"); return 0;
}
base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(base)) return PTR_ERR(base);
priv->cm3_base = base; return 0;
}
staticint mvpp2_probe(struct platform_device *pdev)
{ struct mvpp2 *priv; struct resource *res; void __iomem *base; int i, shared; int err;
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM;
/* multi queue mode isn't supported on PPV2.1, fallback to single * mode
*/ if (priv->hw_version == MVPP21)
queue_mode = MVPP2_QDIST_SINGLE_MODE;
base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) return PTR_ERR(base);
if (priv->hw_version == MVPP21) {
priv->lms_base = devm_platform_ioremap_resource(pdev, 1); if (IS_ERR(priv->lms_base)) return PTR_ERR(priv->lms_base);
} else {
res = platform_get_resource(pdev, IORESOURCE_MEM, 1); if (!res) {
dev_err(&pdev->dev, "Invalid resource\n"); return -EINVAL;
} if (has_acpi_companion(&pdev->dev)) { /* In case the MDIO memory region is declared in * the ACPI, it can already appear as 'in-use' * in the OS. Because it is overlapped by second * region of the network controller, make * sure it is released, before requesting it again. * The care is taken by mvpp2 driver to avoid * concurrent access to this memory region.
*/
release_resource(res);
}
priv->iface_base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(priv->iface_base)) return PTR_ERR(priv->iface_base);
/* Map CM3 SRAM */
err = mvpp2_get_sram(pdev, priv); if (err)
dev_warn(&pdev->dev, "Fail to alloc CM3 SRAM\n");
/* Enable global Flow Control only if handler to SRAM not NULL */ if (priv->cm3_base)
priv->global_tx_fc = true;
}
if (priv->hw_version >= MVPP22 && dev_of_node(&pdev->dev)) {
priv->sysctrl_base =
syscon_regmap_lookup_by_phandle(pdev->dev.of_node, "marvell,system-controller"); if (IS_ERR(priv->sysctrl_base)) /* The system controller regmap is optional for dt * compatibility reasons. When not provided, the * configuration of the GoP relies on the * firmware/bootloader.
*/
priv->sysctrl_base = NULL;
}
if (priv->hw_version >= MVPP22) {
err = dma_set_mask(&pdev->dev, MVPP2_DESC_DMA_MASK); if (err) goto err_axi_clk; /* Sadly, the BM pools all share the same register to * store the high 32 bits of their address. So they * must all have the same high 32 bits, which forces * us to restrict coherent memory to DMA_BIT_MASK(32).
*/
err = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); if (err) goto err_axi_clk;
}
/* Map DTS-active ports. Should be done before FIFO mvpp2_init */
device_for_each_child_node_scoped(&pdev->dev, port_fwnode) { if (!fwnode_property_read_u32(port_fwnode, "port-id", &i))
priv->port_map |= BIT(i);
}
if (mvpp2_read(priv, MVPP2_VER_ID_REG) == MVPP2_VER_PP23)
priv->hw_version = MVPP23;
if (priv->port_count == 0) {
dev_err(&pdev->dev, "no ports enabled\n");
err = -ENODEV; goto err_axi_clk;
}
/* Statistics must be gathered regularly because some of them (like * packets counters) are 32-bit registers and could overflow quite * quickly. For instance, a 10Gb link used at full bandwidth with the * smallest packets (64B) will overflow a 32-bit counter in less than * 30 seconds. Then, use a workqueue to fill 64-bit counters.
*/
snprintf(priv->queue_name, sizeof(priv->queue_name), "stats-wq-%s%s", netdev_name(priv->port_list[0]->dev),
priv->port_count > 1 ? "+" : "");
priv->stats_queue = create_singlethread_workqueue(priv->queue_name); if (!priv->stats_queue) {
err = -ENOMEM; goto err_port_probe;
}
if (priv->global_tx_fc && priv->hw_version >= MVPP22) {
err = mvpp2_enable_global_fc(priv); if (err)
dev_warn(&pdev->dev, "Minimum of CM3 firmware 18.09 and chip revision B0 required for flow control\n");
}
mvpp2_dbgfs_init(priv, pdev->name);
platform_set_drvdata(pdev, priv); return 0;
err_port_probe: for (i = 0; i < priv->port_count; i++)
mvpp2_port_remove(priv->port_list[i]);
err_axi_clk:
clk_disable_unprepare(priv->axi_clk);
err_mg_core_clk:
clk_disable_unprepare(priv->mg_core_clk);
err_mg_clk:
clk_disable_unprepare(priv->mg_clk);
err_gop_clk:
clk_disable_unprepare(priv->gop_clk);
err_pp_clk:
clk_disable_unprepare(priv->pp_clk); return err;
}
staticvoid mvpp2_remove(struct platform_device *pdev)
{ struct mvpp2 *priv = platform_get_drvdata(pdev); int i, poolnum = MVPP2_BM_POOLS_NUM;
mvpp2_dbgfs_cleanup(priv);
for (i = 0; i < priv->port_count; i++) {
mutex_destroy(&priv->port_list[i]->gather_stats_lock);
mvpp2_port_remove(priv->port_list[i]);
}
destroy_workqueue(priv->stats_queue);
if (priv->percpu_pools)
poolnum = mvpp2_get_nrxqs(priv) * 2;
for (i = 0; i < poolnum; i++) { struct mvpp2_bm_pool *bm_pool = &priv->bm_pools[i];
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.233Bemerkung:
(vorverarbeitet am 2026-04-26)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.