/* SPDX-License-Identifier: GPL-2.0+ */ #ifndef _FDMA_API_H_ #define _FDMA_API_H_ #include #include #include /* 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