blob: bc433db0c6d15a1e513195f8c3fc29df7cacf985 [file] [log] [blame]
/*
* aQuantia Corporation Network Driver
* Copyright (C) 2017 aQuantia Corporation. All rights reserved
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*/
#ifndef _ATL_RING_H_
#define _ATL_RING_H_
#include <linux/compiler.h>
#include "atl_common.h"
#include "atl_desc.h"
//#define ATL_RINGS_IN_UC_MEM
#define ATL_TX_DESC_WB
//#define ATL_TX_HEAD_WB
#define ATL_RX_HEADROOM (NET_SKB_PAD + NET_IP_ALIGN)
#define ATL_RX_TAILROOM 64u
#define ATL_RX_HEAD_ORDER 0
#define ATL_RX_DATA_ORDER 0
/* Header space in skb. Must be a multiple of L1_CACHE_BYTES */
#define ATL_RX_HDR_SIZE 256u
#define ATL_RX_HDR_OVRHD SKB_DATA_ALIGN(ATL_RX_HEADROOM + \
SKB_DATA_ALIGN(sizeof(struct skb_shared_info)))
#define ATL_RX_BUF_SIZE 2048
#define ATL_MAX_RX_LINEAR_MTU (ATL_RX_BUF_SIZE - ETH_HLEN)
#define ring_space(ring) \
({ \
typeof(ring) __ring = (ring); \
uint32_t space = READ_ONCE(__ring->head) - \
READ_ONCE(__ring->tail) - 1; \
(int32_t)space < 0 ? space + __ring->hw.size : space; \
})
#define ring_occupied(ring) \
({ \
typeof(ring) __ring = (ring); \
uint32_t occupied = READ_ONCE(__ring->tail) - \
READ_ONCE(__ring->head); \
(int32_t)occupied < 0 ? occupied + __ring->hw.size \
: occupied; \
})
#define bump_ptr(ptr, ring, amount) \
({ \
uint32_t __res = offset_ptr(ptr, ring, amount); \
(ptr) = __res; \
__res; \
})
/* These don't have to be atomic, because Tx tail is only adjusted
* in ndo->start_xmit which is serialized by the stack and the rest are
* only adjusted in NAPI poll which is serialized by NAPI */
#define bump_tail(ring, amount) do { \
uint32_t __ptr = READ_ONCE((ring)->tail); \
WRITE_ONCE((ring)->tail, offset_ptr(__ptr, ring, amount)); \
} while (0)
#define bump_head(ring, amount) do { \
uint32_t __ptr = READ_ONCE((ring)->head); \
WRITE_ONCE((ring)->head, offset_ptr(__ptr, ring, amount)); \
} while (0)
struct atl_rxpage {
struct page *page;
dma_addr_t daddr;
unsigned mapcount; /* not atomic_t because accesses are
* serialized by NAPI */
unsigned order;
};
struct atl_pgref {
struct atl_rxpage *rxpage;
unsigned pg_off;
};
struct atl_cb {
struct atl_pgref pgref;
bool head;
};
#define ATL_CB(skb) ((struct atl_cb *)(skb)->cb)
struct atl_rxbuf {
struct sk_buff *skb;
struct atl_pgref head;
struct atl_pgref data;
};
struct atl_txbuf {
struct sk_buff *skb;
uint32_t last; /* index of eop descriptor */
unsigned bytes;
unsigned packets;
DEFINE_DMA_UNMAP_ADDR(daddr);
DEFINE_DMA_UNMAP_LEN(len);
};
struct atl_desc_ring {
struct atl_hw_ring hw;
uint32_t head, tail;
union {
/* Rx ring only */
uint32_t next_to_recycle;
/* Tx ring only, template desc for atl_map_tx_skb() */
union atl_desc desc;
};
union {
struct atl_rxbuf *rxbufs;
struct atl_txbuf *txbufs;
void *bufs;
};
struct atl_queue_vec *qvec;
struct u64_stats_sync syncp;
struct atl_ring_stats stats;
};
struct atl_queue_vec {
struct atl_desc_ring tx;
struct atl_desc_ring rx;
struct device *dev; /* pdev->dev for DMA */
struct napi_struct napi;
struct atl_nic *nic;
unsigned idx;
char name[IFNAMSIZ + 10];
#ifdef ATL_COMPAT_PCI_ALLOC_IRQ_VECTORS_AFFINITY
cpumask_t affinity_hint;
#endif
};
#define atl_for_each_qvec(nic, qvec) \
for (qvec = &(nic)->qvecs[0]; \
qvec < &(nic)->qvecs[(nic)->nvecs]; qvec++)
static inline struct atl_hw *ring_hw(struct atl_desc_ring *ring)
{
return &ring->qvec->nic->hw;
}
static inline int atl_qvec_intr(struct atl_queue_vec *qvec)
{
return qvec->idx + ATL_NUM_NON_RING_IRQS;
}
static inline void *atl_buf_vaddr(struct atl_pgref *pgref)
{
return page_to_virt(pgref->rxpage->page) + pgref->pg_off;
}
static inline dma_addr_t atl_buf_daddr(struct atl_pgref *pgref)
{
return pgref->rxpage->daddr + pgref->pg_off;
}
void atl_get_ring_stats(struct atl_desc_ring *ring,
struct atl_ring_stats *stats);
#ifdef ATL_RINGS_IN_UC_MEM
#define DECLARE_SCRATCH_DESC(_name) union atl_desc _name
#define DESC_PTR(_ring, _idx, _scratch) (&(_scratch))
#define COMMIT_DESC(_ring, _idx, _scratch) \
WRITE_ONCE((_ring)->hw.descs[_idx], (_scratch))
#define FETCH_DESC(_ring, _idx, _scratch) \
do { \
(_scratch) = READ_ONCE((_ring)->hw.descs[_idx]); \
dma_rmb(); \
} while(0)
#define DESC_RMB()
#else // ATL_RINGS_IN_UC_MEM
#define DECLARE_SCRATCH_DESC(_name)
#define DESC_PTR(_ring, _idx, _scratch) (&(_ring)->hw.descs[_idx])
#define COMMIT_DESC(_ring, _idx, _scratch)
#define FETCH_DESC(_ring, _idx, _scratch)
#define DESC_RMB() dma_rmb()
#endif // ATL_RINGS_IN_UC_MEM
#ifdef ATL_TX_HEAD_WB
#error Head ptr writeback not implemented
#elif !defined(ATL_TX_DESC_WB)
static inline uint32_t atl_get_tx_head(struct atl_desc_ring *ring)
{
return atl_read(ring_hw(ring), ATL_TX_RING_HEAD(ring->idx)) & 0x1fff;
}
#endif
#endif