Commit ff09bc36 authored by David S. Miller's avatar David S. Miller

Merge branch 'sparx5-fdma-part-one'

Daniel Machon says:

====================
net: microchip: add FDMA library and use it for Sparx5

This patch series is the first of a 2-part series, that adds a new
common FDMA library for Microchip switch chips Sparx5 and lan966x. These
chips share the same FDMA engine, and as such will benefit from a
common library with a common implementation.  This also has the benefit
of removing a lot open-coded bookkeeping and duplicate code for the two
drivers.

Additionally, upstreaming efforts for a third chip, lan969x, will begin
in the near future. This chip will use the new library too.

In this first series, the FDMA library is introduced and used by the
Sparx5 switch driver.

 ###################
 # Example of use: #
 ###################

- Initialize the rx and tx fdma structs with values for: number of
  DCB's, number of DB's, channel ID, DB size (data buffer size), and
  total size of the requested memory. Also provide two callbacks:
  nextptr_cb() and dataptr_cb() for getting the nextptr and dataptr.

- Allocate memory using fdma_alloc_phys() or fdma_alloc_coherent().

- Initialize the DCB's with fdma_dcb_init().

- Add new DCB's with fdma_dcb_add().

- Free memory with fdma_free_phys() or fdma_free_coherent().

 #####################
 # Patch  breakdown: #
 #####################

Patch #1:  introduces library and selects it for Sparx5.

Patch #2:  includes the fdma_api.h header and removes old symbols.

Patch #3:  replaces old rx and tx variables with equivalent ones from the
           fdma struct. Only the variables that can be changed without
           breaking traffic is changed in this patch.

Patch #4:  uses the library for allocation of rx buffers. This requires
           quite a bit of refactoring in this single patch.

Patch #5:  uses the library for adding DCB's in the rx path.

Patch #6:  uses the library for freeing rx buffers.

Patch #7:  uses the library helpers in the rx path.

Patch #8:  uses the library for allocation of tx buffers. This requires
           quite a bit of refactoring in this single patch.

Patch #9:  uses the library for adding DCB's in the tx path.

Patch #10: uses the library helpers in the tx path.

Patch #11: ditches the existing linked list for storing buffer addresses,
           and instead uses offsets into contiguous memory.

Patch #12: modifies existing rx and tx functions to be direction
           independent.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 3d4d0fa4 51152312
......@@ -59,5 +59,6 @@ config LAN743X
source "drivers/net/ethernet/microchip/lan966x/Kconfig"
source "drivers/net/ethernet/microchip/sparx5/Kconfig"
source "drivers/net/ethernet/microchip/vcap/Kconfig"
source "drivers/net/ethernet/microchip/fdma/Kconfig"
endif # NET_VENDOR_MICROCHIP
......@@ -12,3 +12,4 @@ lan743x-objs := lan743x_main.o lan743x_ethtool.o lan743x_ptp.o
obj-$(CONFIG_LAN966X_SWITCH) += lan966x/
obj-$(CONFIG_SPARX5_SWITCH) += sparx5/
obj-$(CONFIG_VCAP) += vcap/
obj-$(CONFIG_FDMA) += fdma/
# SPDX-License-Identifier: GPL-2.0-only
#
# Microchip FDMA API configuration
#
if NET_VENDOR_MICROCHIP
config FDMA
bool "FDMA API"
help
Provides the basic FDMA functionality for multiple Microchip
switchcores.
Say Y here if you want to build the FDMA API that provides a common
set of functions and data structures for interacting with the Frame
DMA engine in multiple microchip switchcores.
endif # NET_VENDOR_MICROCHIP
# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for Microchip FDMA
#
obj-$(CONFIG_FDMA) += fdma.o
fdma-y += fdma_api.o
// SPDX-License-Identifier: GPL-2.0+
#include "fdma_api.h"
#include <linux/bits.h>
#include <linux/etherdevice.h>
#include <linux/types.h>
/* Add a DB to a DCB, providing a callback for getting the DB dataptr. */
static int __fdma_db_add(struct fdma *fdma, int dcb_idx, int db_idx, u64 status,
int (*cb)(struct fdma *fdma, int dcb_idx,
int db_idx, u64 *dataptr))
{
struct fdma_db *db = fdma_db_get(fdma, dcb_idx, db_idx);
db->status = status;
return cb(fdma, dcb_idx, db_idx, &db->dataptr);
}
/* Add a DB to a DCB, using the callback set in the fdma_ops struct. */
int fdma_db_add(struct fdma *fdma, int dcb_idx, int db_idx, u64 status)
{
return __fdma_db_add(fdma,
dcb_idx,
db_idx,
status,
fdma->ops.dataptr_cb);
}
/* Add a DCB with callbacks for getting the DB dataptr and the DCB nextptr. */
int __fdma_dcb_add(struct fdma *fdma, int dcb_idx, u64 info, u64 status,
int (*dcb_cb)(struct fdma *fdma, int dcb_idx, u64 *nextptr),
int (*db_cb)(struct fdma *fdma, int dcb_idx, int db_idx,
u64 *dataptr))
{
struct fdma_dcb *dcb = fdma_dcb_get(fdma, dcb_idx);
int i, err;
for (i = 0; i < fdma->n_dbs; i++) {
err = __fdma_db_add(fdma, dcb_idx, i, status, db_cb);
if (unlikely(err))
return err;
}
err = dcb_cb(fdma, dcb_idx, &fdma->last_dcb->nextptr);
if (unlikely(err))
return err;
fdma->last_dcb = dcb;
dcb->nextptr = FDMA_DCB_INVALID_DATA;
dcb->info = info;
return 0;
}
EXPORT_SYMBOL_GPL(__fdma_dcb_add);
/* Add a DCB, using the preset callbacks in the fdma_ops struct. */
int fdma_dcb_add(struct fdma *fdma, int dcb_idx, u64 info, u64 status)
{
return __fdma_dcb_add(fdma,
dcb_idx,
info, status,
fdma->ops.nextptr_cb,
fdma->ops.dataptr_cb);
}
EXPORT_SYMBOL_GPL(fdma_dcb_add);
/* Initialize the DCB's and DB's. */
int fdma_dcbs_init(struct fdma *fdma, u64 info, u64 status)
{
int i, err;
fdma->last_dcb = fdma->dcbs;
fdma->db_index = 0;
fdma->dcb_index = 0;
for (i = 0; i < fdma->n_dcbs; i++) {
err = fdma_dcb_add(fdma, i, info, status);
if (err)
return err;
}
return 0;
}
EXPORT_SYMBOL_GPL(fdma_dcbs_init);
/* Allocate coherent DMA memory for FDMA. */
int fdma_alloc_coherent(struct device *dev, struct fdma *fdma)
{
fdma->dcbs = dma_alloc_coherent(dev,
fdma->size,
&fdma->dma,
GFP_KERNEL);
if (!fdma->dcbs)
return -ENOMEM;
return 0;
}
EXPORT_SYMBOL_GPL(fdma_alloc_coherent);
/* Allocate physical memory for FDMA. */
int fdma_alloc_phys(struct fdma *fdma)
{
fdma->dcbs = kzalloc(fdma->size, GFP_KERNEL);
if (!fdma->dcbs)
return -ENOMEM;
fdma->dma = virt_to_phys(fdma->dcbs);
return 0;
}
EXPORT_SYMBOL_GPL(fdma_alloc_phys);
/* Free coherent DMA memory. */
void fdma_free_coherent(struct device *dev, struct fdma *fdma)
{
dma_free_coherent(dev, fdma->size, fdma->dcbs, fdma->dma);
}
EXPORT_SYMBOL_GPL(fdma_free_coherent);
/* Free virtual memory. */
void fdma_free_phys(struct fdma *fdma)
{
kfree(fdma->dcbs);
}
EXPORT_SYMBOL_GPL(fdma_free_phys);
/* Get the size of the FDMA memory */
u32 fdma_get_size(struct fdma *fdma)
{
return ALIGN(sizeof(struct fdma_dcb) * fdma->n_dcbs, PAGE_SIZE);
}
EXPORT_SYMBOL_GPL(fdma_get_size);
/* Get the size of the FDMA memory. This function is only applicable if the
* dataptr addresses and DCB's are in contiguous memory.
*/
u32 fdma_get_size_contiguous(struct fdma *fdma)
{
return ALIGN(fdma->n_dcbs * sizeof(struct fdma_dcb) +
fdma->n_dcbs * fdma->n_dbs * fdma->db_size,
PAGE_SIZE);
}
EXPORT_SYMBOL_GPL(fdma_get_size_contiguous);
/* SPDX-License-Identifier: GPL-2.0+ */
#ifndef _FDMA_API_H_
#define _FDMA_API_H_
#include <linux/bits.h>
#include <linux/etherdevice.h>
#include <linux/types.h>
/* This provides a common set of functions and data structures for interacting
* with the Frame DMA engine on multiple Microchip switchcores.
*
* Frame DMA DCB format:
*
* +---------------------------+
* | Next Ptr |
* +---------------------------+
* | Reserved | Info |
* +---------------------------+
* | Data0 Ptr |
* +---------------------------+
* | Reserved | Status0 |
* +---------------------------+
* | Data1 Ptr |
* +---------------------------+
* | Reserved | Status1 |
* +---------------------------+
* | Data2 Ptr |
* +---------------------------+
* | Reserved | Status2 |
* |-------------|-------------|
* | |
* | |
* | |
* | |
* | |
* |---------------------------|
* | Data14 Ptr |
* +-------------|-------------+
* | Reserved | Status14 |
* +-------------|-------------+
*
* The data pointers points to the actual frame data to be received or sent. The
* addresses of the data pointers can, as of writing, be either a: DMA address,
* physical address or mapped address.
*
*/
#define FDMA_DCB_INFO_DATAL(x) ((x) & GENMASK(15, 0))
#define FDMA_DCB_INFO_TOKEN BIT(17)
#define FDMA_DCB_INFO_INTR BIT(18)
#define FDMA_DCB_INFO_SW(x) (((x) << 24) & GENMASK(31, 24))
#define FDMA_DCB_STATUS_BLOCKL(x) ((x) & GENMASK(15, 0))
#define FDMA_DCB_STATUS_SOF BIT(16)
#define FDMA_DCB_STATUS_EOF BIT(17)
#define FDMA_DCB_STATUS_INTR BIT(18)
#define FDMA_DCB_STATUS_DONE BIT(19)
#define FDMA_DCB_STATUS_BLOCKO(x) (((x) << 20) & GENMASK(31, 20))
#define FDMA_DCB_INVALID_DATA 0x1
#define FDMA_DB_MAX 15 /* Max number of DB's on Sparx5 */
struct fdma;
struct fdma_db {
u64 dataptr;
u64 status;
};
struct fdma_dcb {
u64 nextptr;
u64 info;
struct fdma_db db[FDMA_DB_MAX];
};
struct fdma_ops {
/* User-provided callback to set the dataptr */
int (*dataptr_cb)(struct fdma *fdma, int dcb_idx, int db_idx, u64 *ptr);
/* User-provided callback to set the nextptr */
int (*nextptr_cb)(struct fdma *fdma, int dcb_idx, u64 *ptr);
};
struct fdma {
void *priv;
/* Virtual addresses */
struct fdma_dcb *dcbs;
struct fdma_dcb *last_dcb;
/* DMA address */
dma_addr_t dma;
/* Size of DCB + DB memory */
int size;
/* Indexes used to access the next-to-be-used DCB or DB */
int db_index;
int dcb_index;
/* Number of DCB's and DB's */
u32 n_dcbs;
u32 n_dbs;
/* Size of DB's */
u32 db_size;
/* Channel id this FDMA object operates on */
u32 channel_id;
struct fdma_ops ops;
};
/* Advance the DCB index and wrap if required. */
static inline void fdma_dcb_advance(struct fdma *fdma)
{
fdma->dcb_index++;
if (fdma->dcb_index >= fdma->n_dcbs)
fdma->dcb_index = 0;
}
/* Advance the DB index. */
static inline void fdma_db_advance(struct fdma *fdma)
{
fdma->db_index++;
}
/* Reset the db index to zero. */
static inline void fdma_db_reset(struct fdma *fdma)
{
fdma->db_index = 0;
}
/* Check if a DCB can be reused in case of multiple DB's per DCB. */
static inline bool fdma_dcb_is_reusable(struct fdma *fdma)
{
return fdma->db_index != fdma->n_dbs;
}
/* Check if the FDMA has marked this DB as done. */
static inline bool fdma_db_is_done(struct fdma_db *db)
{
return db->status & FDMA_DCB_STATUS_DONE;
}
/* Get the length of a DB. */
static inline int fdma_db_len_get(struct fdma_db *db)
{
return FDMA_DCB_STATUS_BLOCKL(db->status);
}
/* Set the length of a DB. */
static inline void fdma_dcb_len_set(struct fdma_dcb *dcb, u32 len)
{
dcb->info = FDMA_DCB_INFO_DATAL(len);
}
/* Get a DB by index. */
static inline struct fdma_db *fdma_db_get(struct fdma *fdma, int dcb_idx,
int db_idx)
{
return &fdma->dcbs[dcb_idx].db[db_idx];
}
/* Get the next DB. */
static inline struct fdma_db *fdma_db_next_get(struct fdma *fdma)
{
return fdma_db_get(fdma, fdma->dcb_index, fdma->db_index);
}
/* Get a DCB by index. */
static inline struct fdma_dcb *fdma_dcb_get(struct fdma *fdma, int dcb_idx)
{
return &fdma->dcbs[dcb_idx];
}
/* Get the next DCB. */
static inline struct fdma_dcb *fdma_dcb_next_get(struct fdma *fdma)
{
return fdma_dcb_get(fdma, fdma->dcb_index);
}
/* Check if the FDMA has frames ready for extraction. */
static inline bool fdma_has_frames(struct fdma *fdma)
{
return fdma_db_is_done(fdma_db_next_get(fdma));
}
/* Get a nextptr by index */
static inline int fdma_nextptr_cb(struct fdma *fdma, int dcb_idx, u64 *nextptr)
{
*nextptr = fdma->dma + (sizeof(struct fdma_dcb) * dcb_idx);
return 0;
}
/* Get the DMA address of a dataptr, by index. This function is only applicable
* if the dataptr addresses and DCB's are in contiguous memory and the driver
* supports XDP.
*/
static inline u64 fdma_dataptr_get_contiguous(struct fdma *fdma, int dcb_idx,
int db_idx)
{
return fdma->dma + (sizeof(struct fdma_dcb) * fdma->n_dcbs) +
(dcb_idx * fdma->n_dbs + db_idx) * fdma->db_size +
XDP_PACKET_HEADROOM;
}
/* Get the virtual address of a dataptr, by index. This function is only
* applicable if the dataptr addresses and DCB's are in contiguous memory and
* the driver supports XDP.
*/
static inline void *fdma_dataptr_virt_get_contiguous(struct fdma *fdma,
int dcb_idx, int db_idx)
{
return (u8 *)fdma->dcbs + (sizeof(struct fdma_dcb) * fdma->n_dcbs) +
(dcb_idx * fdma->n_dbs + db_idx) * fdma->db_size +
XDP_PACKET_HEADROOM;
}
/* Check if this DCB is the last used DCB. */
static inline bool fdma_is_last(struct fdma *fdma, struct fdma_dcb *dcb)
{
return dcb == fdma->last_dcb;
}
int fdma_dcbs_init(struct fdma *fdma, u64 info, u64 status);
int fdma_db_add(struct fdma *fdma, int dcb_idx, int db_idx, u64 status);
int fdma_dcb_add(struct fdma *fdma, int dcb_idx, u64 info, u64 status);
int __fdma_dcb_add(struct fdma *fdma, int dcb_idx, u64 info, u64 status,
int (*dcb_cb)(struct fdma *fdma, int dcb_idx, u64 *nextptr),
int (*db_cb)(struct fdma *fdma, int dcb_idx, int db_idx,
u64 *dataptr));
int fdma_alloc_coherent(struct device *dev, struct fdma *fdma);
int fdma_alloc_phys(struct fdma *fdma);
void fdma_free_coherent(struct device *dev, struct fdma *fdma);
void fdma_free_phys(struct fdma *fdma);
u32 fdma_get_size(struct fdma *fdma);
u32 fdma_get_size_contiguous(struct fdma *fdma);
#endif
......@@ -10,6 +10,7 @@ config SPARX5_SWITCH
select PHY_SPARX5_SERDES
select RESET_CONTROLLER
select VCAP
select FDMA
help
This driver supports the Sparx5 network switch device.
......
......@@ -18,3 +18,4 @@ sparx5-switch-$(CONFIG_DEBUG_FS) += sparx5_vcap_debugfs.o
# Provide include files
ccflags-y += -I$(srctree)/drivers/net/ethernet/microchip/vcap
ccflags-y += -I$(srctree)/drivers/net/ethernet/microchip/fdma
......@@ -20,6 +20,8 @@
#include <linux/debugfs.h>
#include <net/flow_offload.h>
#include <fdma_api.h>
#include "sparx5_main_regs.h"
/* Target chip type */
......@@ -100,23 +102,6 @@ enum sparx5_vlan_port_type {
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
......@@ -124,14 +109,10 @@ struct sparx5_tx_dcb_hw {
* 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 fdma fdma;
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;
};
......@@ -140,11 +121,7 @@ struct sparx5_rx {
* 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;
struct fdma fdma;
u64 packets;
u64 dropped;
};
......
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