162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* Microchip Sparx5 Switch driver 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (c) 2021 Microchip Technology Inc. and its subsidiaries. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * The Sparx5 Chip Register Model can be browsed at this location: 762306a36Sopenharmony_ci * https://github.com/microchip-ung/sparx-5_reginfo 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/types.h> 1162306a36Sopenharmony_ci#include <linux/skbuff.h> 1262306a36Sopenharmony_ci#include <linux/netdevice.h> 1362306a36Sopenharmony_ci#include <linux/interrupt.h> 1462306a36Sopenharmony_ci#include <linux/ip.h> 1562306a36Sopenharmony_ci#include <linux/dma-mapping.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include "sparx5_main_regs.h" 1862306a36Sopenharmony_ci#include "sparx5_main.h" 1962306a36Sopenharmony_ci#include "sparx5_port.h" 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#define FDMA_XTR_CHANNEL 6 2262306a36Sopenharmony_ci#define FDMA_INJ_CHANNEL 0 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#define FDMA_DCB_INFO_DATAL(x) ((x) & GENMASK(15, 0)) 2562306a36Sopenharmony_ci#define FDMA_DCB_INFO_TOKEN BIT(17) 2662306a36Sopenharmony_ci#define FDMA_DCB_INFO_INTR BIT(18) 2762306a36Sopenharmony_ci#define FDMA_DCB_INFO_SW(x) (((x) << 24) & GENMASK(31, 24)) 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#define FDMA_DCB_STATUS_BLOCKL(x) ((x) & GENMASK(15, 0)) 3062306a36Sopenharmony_ci#define FDMA_DCB_STATUS_SOF BIT(16) 3162306a36Sopenharmony_ci#define FDMA_DCB_STATUS_EOF BIT(17) 3262306a36Sopenharmony_ci#define FDMA_DCB_STATUS_INTR BIT(18) 3362306a36Sopenharmony_ci#define FDMA_DCB_STATUS_DONE BIT(19) 3462306a36Sopenharmony_ci#define FDMA_DCB_STATUS_BLOCKO(x) (((x) << 20) & GENMASK(31, 20)) 3562306a36Sopenharmony_ci#define FDMA_DCB_INVALID_DATA 0x1 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#define FDMA_XTR_BUFFER_SIZE 2048 3862306a36Sopenharmony_ci#define FDMA_WEIGHT 4 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci/* Frame DMA DCB format 4162306a36Sopenharmony_ci * 4262306a36Sopenharmony_ci * +---------------------------+ 4362306a36Sopenharmony_ci * | Next Ptr | 4462306a36Sopenharmony_ci * +---------------------------+ 4562306a36Sopenharmony_ci * | Reserved | Info | 4662306a36Sopenharmony_ci * +---------------------------+ 4762306a36Sopenharmony_ci * | Data0 Ptr | 4862306a36Sopenharmony_ci * +---------------------------+ 4962306a36Sopenharmony_ci * | Reserved | Status0 | 5062306a36Sopenharmony_ci * +---------------------------+ 5162306a36Sopenharmony_ci * | Data1 Ptr | 5262306a36Sopenharmony_ci * +---------------------------+ 5362306a36Sopenharmony_ci * | Reserved | Status1 | 5462306a36Sopenharmony_ci * +---------------------------+ 5562306a36Sopenharmony_ci * | Data2 Ptr | 5662306a36Sopenharmony_ci * +---------------------------+ 5762306a36Sopenharmony_ci * | Reserved | Status2 | 5862306a36Sopenharmony_ci * |-------------|-------------| 5962306a36Sopenharmony_ci * | | 6062306a36Sopenharmony_ci * | | 6162306a36Sopenharmony_ci * | | 6262306a36Sopenharmony_ci * | | 6362306a36Sopenharmony_ci * | | 6462306a36Sopenharmony_ci * |---------------------------| 6562306a36Sopenharmony_ci * | Data14 Ptr | 6662306a36Sopenharmony_ci * +-------------|-------------+ 6762306a36Sopenharmony_ci * | Reserved | Status14 | 6862306a36Sopenharmony_ci * +-------------|-------------+ 6962306a36Sopenharmony_ci */ 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci/* For each hardware DB there is an entry in this list and when the HW DB 7262306a36Sopenharmony_ci * entry is used, this SW DB entry is moved to the back of the list 7362306a36Sopenharmony_ci */ 7462306a36Sopenharmony_cistruct sparx5_db { 7562306a36Sopenharmony_ci struct list_head list; 7662306a36Sopenharmony_ci void *cpu_addr; 7762306a36Sopenharmony_ci}; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_cistatic void sparx5_fdma_rx_add_dcb(struct sparx5_rx *rx, 8062306a36Sopenharmony_ci struct sparx5_rx_dcb_hw *dcb, 8162306a36Sopenharmony_ci u64 nextptr) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci int idx = 0; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci /* Reset the status of the DB */ 8662306a36Sopenharmony_ci for (idx = 0; idx < FDMA_RX_DCB_MAX_DBS; ++idx) { 8762306a36Sopenharmony_ci struct sparx5_db_hw *db = &dcb->db[idx]; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci db->status = FDMA_DCB_STATUS_INTR; 9062306a36Sopenharmony_ci } 9162306a36Sopenharmony_ci dcb->nextptr = FDMA_DCB_INVALID_DATA; 9262306a36Sopenharmony_ci dcb->info = FDMA_DCB_INFO_DATAL(FDMA_XTR_BUFFER_SIZE); 9362306a36Sopenharmony_ci rx->last_entry->nextptr = nextptr; 9462306a36Sopenharmony_ci rx->last_entry = dcb; 9562306a36Sopenharmony_ci} 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_cistatic void sparx5_fdma_tx_add_dcb(struct sparx5_tx *tx, 9862306a36Sopenharmony_ci struct sparx5_tx_dcb_hw *dcb, 9962306a36Sopenharmony_ci u64 nextptr) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci int idx = 0; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci /* Reset the status of the DB */ 10462306a36Sopenharmony_ci for (idx = 0; idx < FDMA_TX_DCB_MAX_DBS; ++idx) { 10562306a36Sopenharmony_ci struct sparx5_db_hw *db = &dcb->db[idx]; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci db->status = FDMA_DCB_STATUS_DONE; 10862306a36Sopenharmony_ci } 10962306a36Sopenharmony_ci dcb->nextptr = FDMA_DCB_INVALID_DATA; 11062306a36Sopenharmony_ci dcb->info = FDMA_DCB_INFO_DATAL(FDMA_XTR_BUFFER_SIZE); 11162306a36Sopenharmony_ci} 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_cistatic void sparx5_fdma_rx_activate(struct sparx5 *sparx5, struct sparx5_rx *rx) 11462306a36Sopenharmony_ci{ 11562306a36Sopenharmony_ci /* Write the buffer address in the LLP and LLP1 regs */ 11662306a36Sopenharmony_ci spx5_wr(((u64)rx->dma) & GENMASK(31, 0), sparx5, 11762306a36Sopenharmony_ci FDMA_DCB_LLP(rx->channel_id)); 11862306a36Sopenharmony_ci spx5_wr(((u64)rx->dma) >> 32, sparx5, FDMA_DCB_LLP1(rx->channel_id)); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci /* Set the number of RX DBs to be used, and DB end-of-frame interrupt */ 12162306a36Sopenharmony_ci spx5_wr(FDMA_CH_CFG_CH_DCB_DB_CNT_SET(FDMA_RX_DCB_MAX_DBS) | 12262306a36Sopenharmony_ci FDMA_CH_CFG_CH_INTR_DB_EOF_ONLY_SET(1) | 12362306a36Sopenharmony_ci FDMA_CH_CFG_CH_INJ_PORT_SET(XTR_QUEUE), 12462306a36Sopenharmony_ci sparx5, FDMA_CH_CFG(rx->channel_id)); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci /* Set the RX Watermark to max */ 12762306a36Sopenharmony_ci spx5_rmw(FDMA_XTR_CFG_XTR_FIFO_WM_SET(31), FDMA_XTR_CFG_XTR_FIFO_WM, 12862306a36Sopenharmony_ci sparx5, 12962306a36Sopenharmony_ci FDMA_XTR_CFG); 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci /* Start RX fdma */ 13262306a36Sopenharmony_ci spx5_rmw(FDMA_PORT_CTRL_XTR_STOP_SET(0), FDMA_PORT_CTRL_XTR_STOP, 13362306a36Sopenharmony_ci sparx5, FDMA_PORT_CTRL(0)); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci /* Enable RX channel DB interrupt */ 13662306a36Sopenharmony_ci spx5_rmw(BIT(rx->channel_id), 13762306a36Sopenharmony_ci BIT(rx->channel_id) & FDMA_INTR_DB_ENA_INTR_DB_ENA, 13862306a36Sopenharmony_ci sparx5, FDMA_INTR_DB_ENA); 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci /* Activate the RX channel */ 14162306a36Sopenharmony_ci spx5_wr(BIT(rx->channel_id), sparx5, FDMA_CH_ACTIVATE); 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cistatic void sparx5_fdma_rx_deactivate(struct sparx5 *sparx5, struct sparx5_rx *rx) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci /* Dectivate the RX channel */ 14762306a36Sopenharmony_ci spx5_rmw(0, BIT(rx->channel_id) & FDMA_CH_ACTIVATE_CH_ACTIVATE, 14862306a36Sopenharmony_ci sparx5, FDMA_CH_ACTIVATE); 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci /* Disable RX channel DB interrupt */ 15162306a36Sopenharmony_ci spx5_rmw(0, BIT(rx->channel_id) & FDMA_INTR_DB_ENA_INTR_DB_ENA, 15262306a36Sopenharmony_ci sparx5, FDMA_INTR_DB_ENA); 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci /* Stop RX fdma */ 15562306a36Sopenharmony_ci spx5_rmw(FDMA_PORT_CTRL_XTR_STOP_SET(1), FDMA_PORT_CTRL_XTR_STOP, 15662306a36Sopenharmony_ci sparx5, FDMA_PORT_CTRL(0)); 15762306a36Sopenharmony_ci} 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_cistatic void sparx5_fdma_tx_activate(struct sparx5 *sparx5, struct sparx5_tx *tx) 16062306a36Sopenharmony_ci{ 16162306a36Sopenharmony_ci /* Write the buffer address in the LLP and LLP1 regs */ 16262306a36Sopenharmony_ci spx5_wr(((u64)tx->dma) & GENMASK(31, 0), sparx5, 16362306a36Sopenharmony_ci FDMA_DCB_LLP(tx->channel_id)); 16462306a36Sopenharmony_ci spx5_wr(((u64)tx->dma) >> 32, sparx5, FDMA_DCB_LLP1(tx->channel_id)); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci /* Set the number of TX DBs to be used, and DB end-of-frame interrupt */ 16762306a36Sopenharmony_ci spx5_wr(FDMA_CH_CFG_CH_DCB_DB_CNT_SET(FDMA_TX_DCB_MAX_DBS) | 16862306a36Sopenharmony_ci FDMA_CH_CFG_CH_INTR_DB_EOF_ONLY_SET(1) | 16962306a36Sopenharmony_ci FDMA_CH_CFG_CH_INJ_PORT_SET(INJ_QUEUE), 17062306a36Sopenharmony_ci sparx5, FDMA_CH_CFG(tx->channel_id)); 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci /* Start TX fdma */ 17362306a36Sopenharmony_ci spx5_rmw(FDMA_PORT_CTRL_INJ_STOP_SET(0), FDMA_PORT_CTRL_INJ_STOP, 17462306a36Sopenharmony_ci sparx5, FDMA_PORT_CTRL(0)); 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci /* Activate the channel */ 17762306a36Sopenharmony_ci spx5_wr(BIT(tx->channel_id), sparx5, FDMA_CH_ACTIVATE); 17862306a36Sopenharmony_ci} 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_cistatic void sparx5_fdma_tx_deactivate(struct sparx5 *sparx5, struct sparx5_tx *tx) 18162306a36Sopenharmony_ci{ 18262306a36Sopenharmony_ci /* Disable the channel */ 18362306a36Sopenharmony_ci spx5_rmw(0, BIT(tx->channel_id) & FDMA_CH_ACTIVATE_CH_ACTIVATE, 18462306a36Sopenharmony_ci sparx5, FDMA_CH_ACTIVATE); 18562306a36Sopenharmony_ci} 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_cistatic void sparx5_fdma_rx_reload(struct sparx5 *sparx5, struct sparx5_rx *rx) 18862306a36Sopenharmony_ci{ 18962306a36Sopenharmony_ci /* Reload the RX channel */ 19062306a36Sopenharmony_ci spx5_wr(BIT(rx->channel_id), sparx5, FDMA_CH_RELOAD); 19162306a36Sopenharmony_ci} 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_cistatic void sparx5_fdma_tx_reload(struct sparx5 *sparx5, struct sparx5_tx *tx) 19462306a36Sopenharmony_ci{ 19562306a36Sopenharmony_ci /* Reload the TX channel */ 19662306a36Sopenharmony_ci spx5_wr(BIT(tx->channel_id), sparx5, FDMA_CH_RELOAD); 19762306a36Sopenharmony_ci} 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_cistatic struct sk_buff *sparx5_fdma_rx_alloc_skb(struct sparx5_rx *rx) 20062306a36Sopenharmony_ci{ 20162306a36Sopenharmony_ci return __netdev_alloc_skb(rx->ndev, FDMA_XTR_BUFFER_SIZE, 20262306a36Sopenharmony_ci GFP_ATOMIC); 20362306a36Sopenharmony_ci} 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_cistatic bool sparx5_fdma_rx_get_frame(struct sparx5 *sparx5, struct sparx5_rx *rx) 20662306a36Sopenharmony_ci{ 20762306a36Sopenharmony_ci struct sparx5_db_hw *db_hw; 20862306a36Sopenharmony_ci unsigned int packet_size; 20962306a36Sopenharmony_ci struct sparx5_port *port; 21062306a36Sopenharmony_ci struct sk_buff *new_skb; 21162306a36Sopenharmony_ci struct frame_info fi; 21262306a36Sopenharmony_ci struct sk_buff *skb; 21362306a36Sopenharmony_ci dma_addr_t dma_addr; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci /* Check if the DCB is done */ 21662306a36Sopenharmony_ci db_hw = &rx->dcb_entries[rx->dcb_index].db[rx->db_index]; 21762306a36Sopenharmony_ci if (unlikely(!(db_hw->status & FDMA_DCB_STATUS_DONE))) 21862306a36Sopenharmony_ci return false; 21962306a36Sopenharmony_ci skb = rx->skb[rx->dcb_index][rx->db_index]; 22062306a36Sopenharmony_ci /* Replace the DB entry with a new SKB */ 22162306a36Sopenharmony_ci new_skb = sparx5_fdma_rx_alloc_skb(rx); 22262306a36Sopenharmony_ci if (unlikely(!new_skb)) 22362306a36Sopenharmony_ci return false; 22462306a36Sopenharmony_ci /* Map the new skb data and set the new skb */ 22562306a36Sopenharmony_ci dma_addr = virt_to_phys(new_skb->data); 22662306a36Sopenharmony_ci rx->skb[rx->dcb_index][rx->db_index] = new_skb; 22762306a36Sopenharmony_ci db_hw->dataptr = dma_addr; 22862306a36Sopenharmony_ci packet_size = FDMA_DCB_STATUS_BLOCKL(db_hw->status); 22962306a36Sopenharmony_ci skb_put(skb, packet_size); 23062306a36Sopenharmony_ci /* Now do the normal processing of the skb */ 23162306a36Sopenharmony_ci sparx5_ifh_parse((u32 *)skb->data, &fi); 23262306a36Sopenharmony_ci /* Map to port netdev */ 23362306a36Sopenharmony_ci port = fi.src_port < SPX5_PORTS ? sparx5->ports[fi.src_port] : NULL; 23462306a36Sopenharmony_ci if (!port || !port->ndev) { 23562306a36Sopenharmony_ci dev_err(sparx5->dev, "Data on inactive port %d\n", fi.src_port); 23662306a36Sopenharmony_ci sparx5_xtr_flush(sparx5, XTR_QUEUE); 23762306a36Sopenharmony_ci return false; 23862306a36Sopenharmony_ci } 23962306a36Sopenharmony_ci skb->dev = port->ndev; 24062306a36Sopenharmony_ci skb_pull(skb, IFH_LEN * sizeof(u32)); 24162306a36Sopenharmony_ci if (likely(!(skb->dev->features & NETIF_F_RXFCS))) 24262306a36Sopenharmony_ci skb_trim(skb, skb->len - ETH_FCS_LEN); 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci sparx5_ptp_rxtstamp(sparx5, skb, fi.timestamp); 24562306a36Sopenharmony_ci skb->protocol = eth_type_trans(skb, skb->dev); 24662306a36Sopenharmony_ci /* Everything we see on an interface that is in the HW bridge 24762306a36Sopenharmony_ci * has already been forwarded 24862306a36Sopenharmony_ci */ 24962306a36Sopenharmony_ci if (test_bit(port->portno, sparx5->bridge_mask)) 25062306a36Sopenharmony_ci skb->offload_fwd_mark = 1; 25162306a36Sopenharmony_ci skb->dev->stats.rx_bytes += skb->len; 25262306a36Sopenharmony_ci skb->dev->stats.rx_packets++; 25362306a36Sopenharmony_ci rx->packets++; 25462306a36Sopenharmony_ci netif_receive_skb(skb); 25562306a36Sopenharmony_ci return true; 25662306a36Sopenharmony_ci} 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_cistatic int sparx5_fdma_napi_callback(struct napi_struct *napi, int weight) 25962306a36Sopenharmony_ci{ 26062306a36Sopenharmony_ci struct sparx5_rx *rx = container_of(napi, struct sparx5_rx, napi); 26162306a36Sopenharmony_ci struct sparx5 *sparx5 = container_of(rx, struct sparx5, rx); 26262306a36Sopenharmony_ci int counter = 0; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci while (counter < weight && sparx5_fdma_rx_get_frame(sparx5, rx)) { 26562306a36Sopenharmony_ci struct sparx5_rx_dcb_hw *old_dcb; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci rx->db_index++; 26862306a36Sopenharmony_ci counter++; 26962306a36Sopenharmony_ci /* Check if the DCB can be reused */ 27062306a36Sopenharmony_ci if (rx->db_index != FDMA_RX_DCB_MAX_DBS) 27162306a36Sopenharmony_ci continue; 27262306a36Sopenharmony_ci /* As the DCB can be reused, just advance the dcb_index 27362306a36Sopenharmony_ci * pointer and set the nextptr in the DCB 27462306a36Sopenharmony_ci */ 27562306a36Sopenharmony_ci rx->db_index = 0; 27662306a36Sopenharmony_ci old_dcb = &rx->dcb_entries[rx->dcb_index]; 27762306a36Sopenharmony_ci rx->dcb_index++; 27862306a36Sopenharmony_ci rx->dcb_index &= FDMA_DCB_MAX - 1; 27962306a36Sopenharmony_ci sparx5_fdma_rx_add_dcb(rx, old_dcb, 28062306a36Sopenharmony_ci rx->dma + 28162306a36Sopenharmony_ci ((unsigned long)old_dcb - 28262306a36Sopenharmony_ci (unsigned long)rx->dcb_entries)); 28362306a36Sopenharmony_ci } 28462306a36Sopenharmony_ci if (counter < weight) { 28562306a36Sopenharmony_ci napi_complete_done(&rx->napi, counter); 28662306a36Sopenharmony_ci spx5_rmw(BIT(rx->channel_id), 28762306a36Sopenharmony_ci BIT(rx->channel_id) & FDMA_INTR_DB_ENA_INTR_DB_ENA, 28862306a36Sopenharmony_ci sparx5, FDMA_INTR_DB_ENA); 28962306a36Sopenharmony_ci } 29062306a36Sopenharmony_ci if (counter) 29162306a36Sopenharmony_ci sparx5_fdma_rx_reload(sparx5, rx); 29262306a36Sopenharmony_ci return counter; 29362306a36Sopenharmony_ci} 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_cistatic struct sparx5_tx_dcb_hw *sparx5_fdma_next_dcb(struct sparx5_tx *tx, 29662306a36Sopenharmony_ci struct sparx5_tx_dcb_hw *dcb) 29762306a36Sopenharmony_ci{ 29862306a36Sopenharmony_ci struct sparx5_tx_dcb_hw *next_dcb; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci next_dcb = dcb; 30162306a36Sopenharmony_ci next_dcb++; 30262306a36Sopenharmony_ci /* Handle wrap-around */ 30362306a36Sopenharmony_ci if ((unsigned long)next_dcb >= 30462306a36Sopenharmony_ci ((unsigned long)tx->first_entry + FDMA_DCB_MAX * sizeof(*dcb))) 30562306a36Sopenharmony_ci next_dcb = tx->first_entry; 30662306a36Sopenharmony_ci return next_dcb; 30762306a36Sopenharmony_ci} 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ciint sparx5_fdma_xmit(struct sparx5 *sparx5, u32 *ifh, struct sk_buff *skb) 31062306a36Sopenharmony_ci{ 31162306a36Sopenharmony_ci struct sparx5_tx_dcb_hw *next_dcb_hw; 31262306a36Sopenharmony_ci struct sparx5_tx *tx = &sparx5->tx; 31362306a36Sopenharmony_ci static bool first_time = true; 31462306a36Sopenharmony_ci struct sparx5_db_hw *db_hw; 31562306a36Sopenharmony_ci struct sparx5_db *db; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci next_dcb_hw = sparx5_fdma_next_dcb(tx, tx->curr_entry); 31862306a36Sopenharmony_ci db_hw = &next_dcb_hw->db[0]; 31962306a36Sopenharmony_ci if (!(db_hw->status & FDMA_DCB_STATUS_DONE)) 32062306a36Sopenharmony_ci return -EINVAL; 32162306a36Sopenharmony_ci db = list_first_entry(&tx->db_list, struct sparx5_db, list); 32262306a36Sopenharmony_ci list_move_tail(&db->list, &tx->db_list); 32362306a36Sopenharmony_ci next_dcb_hw->nextptr = FDMA_DCB_INVALID_DATA; 32462306a36Sopenharmony_ci tx->curr_entry->nextptr = tx->dma + 32562306a36Sopenharmony_ci ((unsigned long)next_dcb_hw - 32662306a36Sopenharmony_ci (unsigned long)tx->first_entry); 32762306a36Sopenharmony_ci tx->curr_entry = next_dcb_hw; 32862306a36Sopenharmony_ci memset(db->cpu_addr, 0, FDMA_XTR_BUFFER_SIZE); 32962306a36Sopenharmony_ci memcpy(db->cpu_addr, ifh, IFH_LEN * 4); 33062306a36Sopenharmony_ci memcpy(db->cpu_addr + IFH_LEN * 4, skb->data, skb->len); 33162306a36Sopenharmony_ci db_hw->status = FDMA_DCB_STATUS_SOF | 33262306a36Sopenharmony_ci FDMA_DCB_STATUS_EOF | 33362306a36Sopenharmony_ci FDMA_DCB_STATUS_BLOCKO(0) | 33462306a36Sopenharmony_ci FDMA_DCB_STATUS_BLOCKL(skb->len + IFH_LEN * 4 + 4); 33562306a36Sopenharmony_ci if (first_time) { 33662306a36Sopenharmony_ci sparx5_fdma_tx_activate(sparx5, tx); 33762306a36Sopenharmony_ci first_time = false; 33862306a36Sopenharmony_ci } else { 33962306a36Sopenharmony_ci sparx5_fdma_tx_reload(sparx5, tx); 34062306a36Sopenharmony_ci } 34162306a36Sopenharmony_ci return NETDEV_TX_OK; 34262306a36Sopenharmony_ci} 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_cistatic int sparx5_fdma_rx_alloc(struct sparx5 *sparx5) 34562306a36Sopenharmony_ci{ 34662306a36Sopenharmony_ci struct sparx5_rx *rx = &sparx5->rx; 34762306a36Sopenharmony_ci struct sparx5_rx_dcb_hw *dcb; 34862306a36Sopenharmony_ci int idx, jdx; 34962306a36Sopenharmony_ci int size; 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci size = sizeof(struct sparx5_rx_dcb_hw) * FDMA_DCB_MAX; 35262306a36Sopenharmony_ci size = ALIGN(size, PAGE_SIZE); 35362306a36Sopenharmony_ci rx->dcb_entries = devm_kzalloc(sparx5->dev, size, GFP_KERNEL); 35462306a36Sopenharmony_ci if (!rx->dcb_entries) 35562306a36Sopenharmony_ci return -ENOMEM; 35662306a36Sopenharmony_ci rx->dma = virt_to_phys(rx->dcb_entries); 35762306a36Sopenharmony_ci rx->last_entry = rx->dcb_entries; 35862306a36Sopenharmony_ci rx->db_index = 0; 35962306a36Sopenharmony_ci rx->dcb_index = 0; 36062306a36Sopenharmony_ci /* Now for each dcb allocate the db */ 36162306a36Sopenharmony_ci for (idx = 0; idx < FDMA_DCB_MAX; ++idx) { 36262306a36Sopenharmony_ci dcb = &rx->dcb_entries[idx]; 36362306a36Sopenharmony_ci dcb->info = 0; 36462306a36Sopenharmony_ci /* For each db allocate an skb and map skb data pointer to the DB 36562306a36Sopenharmony_ci * dataptr. In this way when the frame is received the skb->data 36662306a36Sopenharmony_ci * will contain the frame, so no memcpy is needed 36762306a36Sopenharmony_ci */ 36862306a36Sopenharmony_ci for (jdx = 0; jdx < FDMA_RX_DCB_MAX_DBS; ++jdx) { 36962306a36Sopenharmony_ci struct sparx5_db_hw *db_hw = &dcb->db[jdx]; 37062306a36Sopenharmony_ci dma_addr_t dma_addr; 37162306a36Sopenharmony_ci struct sk_buff *skb; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci skb = sparx5_fdma_rx_alloc_skb(rx); 37462306a36Sopenharmony_ci if (!skb) 37562306a36Sopenharmony_ci return -ENOMEM; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci dma_addr = virt_to_phys(skb->data); 37862306a36Sopenharmony_ci db_hw->dataptr = dma_addr; 37962306a36Sopenharmony_ci db_hw->status = 0; 38062306a36Sopenharmony_ci rx->skb[idx][jdx] = skb; 38162306a36Sopenharmony_ci } 38262306a36Sopenharmony_ci sparx5_fdma_rx_add_dcb(rx, dcb, rx->dma + sizeof(*dcb) * idx); 38362306a36Sopenharmony_ci } 38462306a36Sopenharmony_ci netif_napi_add_weight(rx->ndev, &rx->napi, sparx5_fdma_napi_callback, 38562306a36Sopenharmony_ci FDMA_WEIGHT); 38662306a36Sopenharmony_ci napi_enable(&rx->napi); 38762306a36Sopenharmony_ci sparx5_fdma_rx_activate(sparx5, rx); 38862306a36Sopenharmony_ci return 0; 38962306a36Sopenharmony_ci} 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_cistatic int sparx5_fdma_tx_alloc(struct sparx5 *sparx5) 39262306a36Sopenharmony_ci{ 39362306a36Sopenharmony_ci struct sparx5_tx *tx = &sparx5->tx; 39462306a36Sopenharmony_ci struct sparx5_tx_dcb_hw *dcb; 39562306a36Sopenharmony_ci int idx, jdx; 39662306a36Sopenharmony_ci int size; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci size = sizeof(struct sparx5_tx_dcb_hw) * FDMA_DCB_MAX; 39962306a36Sopenharmony_ci size = ALIGN(size, PAGE_SIZE); 40062306a36Sopenharmony_ci tx->curr_entry = devm_kzalloc(sparx5->dev, size, GFP_KERNEL); 40162306a36Sopenharmony_ci if (!tx->curr_entry) 40262306a36Sopenharmony_ci return -ENOMEM; 40362306a36Sopenharmony_ci tx->dma = virt_to_phys(tx->curr_entry); 40462306a36Sopenharmony_ci tx->first_entry = tx->curr_entry; 40562306a36Sopenharmony_ci INIT_LIST_HEAD(&tx->db_list); 40662306a36Sopenharmony_ci /* Now for each dcb allocate the db */ 40762306a36Sopenharmony_ci for (idx = 0; idx < FDMA_DCB_MAX; ++idx) { 40862306a36Sopenharmony_ci dcb = &tx->curr_entry[idx]; 40962306a36Sopenharmony_ci dcb->info = 0; 41062306a36Sopenharmony_ci /* TX databuffers must be 16byte aligned */ 41162306a36Sopenharmony_ci for (jdx = 0; jdx < FDMA_TX_DCB_MAX_DBS; ++jdx) { 41262306a36Sopenharmony_ci struct sparx5_db_hw *db_hw = &dcb->db[jdx]; 41362306a36Sopenharmony_ci struct sparx5_db *db; 41462306a36Sopenharmony_ci dma_addr_t phys; 41562306a36Sopenharmony_ci void *cpu_addr; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci cpu_addr = devm_kzalloc(sparx5->dev, 41862306a36Sopenharmony_ci FDMA_XTR_BUFFER_SIZE, 41962306a36Sopenharmony_ci GFP_KERNEL); 42062306a36Sopenharmony_ci if (!cpu_addr) 42162306a36Sopenharmony_ci return -ENOMEM; 42262306a36Sopenharmony_ci phys = virt_to_phys(cpu_addr); 42362306a36Sopenharmony_ci db_hw->dataptr = phys; 42462306a36Sopenharmony_ci db_hw->status = 0; 42562306a36Sopenharmony_ci db = devm_kzalloc(sparx5->dev, sizeof(*db), GFP_KERNEL); 42662306a36Sopenharmony_ci if (!db) 42762306a36Sopenharmony_ci return -ENOMEM; 42862306a36Sopenharmony_ci db->cpu_addr = cpu_addr; 42962306a36Sopenharmony_ci list_add_tail(&db->list, &tx->db_list); 43062306a36Sopenharmony_ci } 43162306a36Sopenharmony_ci sparx5_fdma_tx_add_dcb(tx, dcb, tx->dma + sizeof(*dcb) * idx); 43262306a36Sopenharmony_ci /* Let the curr_entry to point to the last allocated entry */ 43362306a36Sopenharmony_ci if (idx == FDMA_DCB_MAX - 1) 43462306a36Sopenharmony_ci tx->curr_entry = dcb; 43562306a36Sopenharmony_ci } 43662306a36Sopenharmony_ci return 0; 43762306a36Sopenharmony_ci} 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_cistatic void sparx5_fdma_rx_init(struct sparx5 *sparx5, 44062306a36Sopenharmony_ci struct sparx5_rx *rx, int channel) 44162306a36Sopenharmony_ci{ 44262306a36Sopenharmony_ci int idx; 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci rx->channel_id = channel; 44562306a36Sopenharmony_ci /* Fetch a netdev for SKB and NAPI use, any will do */ 44662306a36Sopenharmony_ci for (idx = 0; idx < SPX5_PORTS; ++idx) { 44762306a36Sopenharmony_ci struct sparx5_port *port = sparx5->ports[idx]; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci if (port && port->ndev) { 45062306a36Sopenharmony_ci rx->ndev = port->ndev; 45162306a36Sopenharmony_ci break; 45262306a36Sopenharmony_ci } 45362306a36Sopenharmony_ci } 45462306a36Sopenharmony_ci} 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_cistatic void sparx5_fdma_tx_init(struct sparx5 *sparx5, 45762306a36Sopenharmony_ci struct sparx5_tx *tx, int channel) 45862306a36Sopenharmony_ci{ 45962306a36Sopenharmony_ci tx->channel_id = channel; 46062306a36Sopenharmony_ci} 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ciirqreturn_t sparx5_fdma_handler(int irq, void *args) 46362306a36Sopenharmony_ci{ 46462306a36Sopenharmony_ci struct sparx5 *sparx5 = args; 46562306a36Sopenharmony_ci u32 db = 0, err = 0; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci db = spx5_rd(sparx5, FDMA_INTR_DB); 46862306a36Sopenharmony_ci err = spx5_rd(sparx5, FDMA_INTR_ERR); 46962306a36Sopenharmony_ci /* Clear interrupt */ 47062306a36Sopenharmony_ci if (db) { 47162306a36Sopenharmony_ci spx5_wr(0, sparx5, FDMA_INTR_DB_ENA); 47262306a36Sopenharmony_ci spx5_wr(db, sparx5, FDMA_INTR_DB); 47362306a36Sopenharmony_ci napi_schedule(&sparx5->rx.napi); 47462306a36Sopenharmony_ci } 47562306a36Sopenharmony_ci if (err) { 47662306a36Sopenharmony_ci u32 err_type = spx5_rd(sparx5, FDMA_ERRORS); 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci dev_err_ratelimited(sparx5->dev, 47962306a36Sopenharmony_ci "ERR: int: %#x, type: %#x\n", 48062306a36Sopenharmony_ci err, err_type); 48162306a36Sopenharmony_ci spx5_wr(err, sparx5, FDMA_INTR_ERR); 48262306a36Sopenharmony_ci spx5_wr(err_type, sparx5, FDMA_ERRORS); 48362306a36Sopenharmony_ci } 48462306a36Sopenharmony_ci return IRQ_HANDLED; 48562306a36Sopenharmony_ci} 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_cistatic void sparx5_fdma_injection_mode(struct sparx5 *sparx5) 48862306a36Sopenharmony_ci{ 48962306a36Sopenharmony_ci const int byte_swap = 1; 49062306a36Sopenharmony_ci int portno; 49162306a36Sopenharmony_ci int urgency; 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci /* Change mode to fdma extraction and injection */ 49462306a36Sopenharmony_ci spx5_wr(QS_XTR_GRP_CFG_MODE_SET(2) | 49562306a36Sopenharmony_ci QS_XTR_GRP_CFG_STATUS_WORD_POS_SET(1) | 49662306a36Sopenharmony_ci QS_XTR_GRP_CFG_BYTE_SWAP_SET(byte_swap), 49762306a36Sopenharmony_ci sparx5, QS_XTR_GRP_CFG(XTR_QUEUE)); 49862306a36Sopenharmony_ci spx5_wr(QS_INJ_GRP_CFG_MODE_SET(2) | 49962306a36Sopenharmony_ci QS_INJ_GRP_CFG_BYTE_SWAP_SET(byte_swap), 50062306a36Sopenharmony_ci sparx5, QS_INJ_GRP_CFG(INJ_QUEUE)); 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci /* CPU ports capture setup */ 50362306a36Sopenharmony_ci for (portno = SPX5_PORT_CPU_0; portno <= SPX5_PORT_CPU_1; portno++) { 50462306a36Sopenharmony_ci /* ASM CPU port: No preamble, IFH, enable padding */ 50562306a36Sopenharmony_ci spx5_wr(ASM_PORT_CFG_PAD_ENA_SET(1) | 50662306a36Sopenharmony_ci ASM_PORT_CFG_NO_PREAMBLE_ENA_SET(1) | 50762306a36Sopenharmony_ci ASM_PORT_CFG_INJ_FORMAT_CFG_SET(1), /* 1 = IFH */ 50862306a36Sopenharmony_ci sparx5, ASM_PORT_CFG(portno)); 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci /* Reset WM cnt to unclog queued frames */ 51162306a36Sopenharmony_ci spx5_rmw(DSM_DEV_TX_STOP_WM_CFG_DEV_TX_CNT_CLR_SET(1), 51262306a36Sopenharmony_ci DSM_DEV_TX_STOP_WM_CFG_DEV_TX_CNT_CLR, 51362306a36Sopenharmony_ci sparx5, 51462306a36Sopenharmony_ci DSM_DEV_TX_STOP_WM_CFG(portno)); 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci /* Set Disassembler Stop Watermark level */ 51762306a36Sopenharmony_ci spx5_rmw(DSM_DEV_TX_STOP_WM_CFG_DEV_TX_STOP_WM_SET(100), 51862306a36Sopenharmony_ci DSM_DEV_TX_STOP_WM_CFG_DEV_TX_STOP_WM, 51962306a36Sopenharmony_ci sparx5, 52062306a36Sopenharmony_ci DSM_DEV_TX_STOP_WM_CFG(portno)); 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci /* Enable port in queue system */ 52362306a36Sopenharmony_ci urgency = sparx5_port_fwd_urg(sparx5, SPEED_2500); 52462306a36Sopenharmony_ci spx5_rmw(QFWD_SWITCH_PORT_MODE_PORT_ENA_SET(1) | 52562306a36Sopenharmony_ci QFWD_SWITCH_PORT_MODE_FWD_URGENCY_SET(urgency), 52662306a36Sopenharmony_ci QFWD_SWITCH_PORT_MODE_PORT_ENA | 52762306a36Sopenharmony_ci QFWD_SWITCH_PORT_MODE_FWD_URGENCY, 52862306a36Sopenharmony_ci sparx5, 52962306a36Sopenharmony_ci QFWD_SWITCH_PORT_MODE(portno)); 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci /* Disable Disassembler buffer underrun watchdog 53262306a36Sopenharmony_ci * to avoid truncated packets in XTR 53362306a36Sopenharmony_ci */ 53462306a36Sopenharmony_ci spx5_rmw(DSM_BUF_CFG_UNDERFLOW_WATCHDOG_DIS_SET(1), 53562306a36Sopenharmony_ci DSM_BUF_CFG_UNDERFLOW_WATCHDOG_DIS, 53662306a36Sopenharmony_ci sparx5, 53762306a36Sopenharmony_ci DSM_BUF_CFG(portno)); 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci /* Disabling frame aging */ 54062306a36Sopenharmony_ci spx5_rmw(HSCH_PORT_MODE_AGE_DIS_SET(1), 54162306a36Sopenharmony_ci HSCH_PORT_MODE_AGE_DIS, 54262306a36Sopenharmony_ci sparx5, 54362306a36Sopenharmony_ci HSCH_PORT_MODE(portno)); 54462306a36Sopenharmony_ci } 54562306a36Sopenharmony_ci} 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ciint sparx5_fdma_start(struct sparx5 *sparx5) 54862306a36Sopenharmony_ci{ 54962306a36Sopenharmony_ci int err; 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci /* Reset FDMA state */ 55262306a36Sopenharmony_ci spx5_wr(FDMA_CTRL_NRESET_SET(0), sparx5, FDMA_CTRL); 55362306a36Sopenharmony_ci spx5_wr(FDMA_CTRL_NRESET_SET(1), sparx5, FDMA_CTRL); 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci /* Force ACP caching but disable read/write allocation */ 55662306a36Sopenharmony_ci spx5_rmw(CPU_PROC_CTRL_ACP_CACHE_FORCE_ENA_SET(1) | 55762306a36Sopenharmony_ci CPU_PROC_CTRL_ACP_AWCACHE_SET(0) | 55862306a36Sopenharmony_ci CPU_PROC_CTRL_ACP_ARCACHE_SET(0), 55962306a36Sopenharmony_ci CPU_PROC_CTRL_ACP_CACHE_FORCE_ENA | 56062306a36Sopenharmony_ci CPU_PROC_CTRL_ACP_AWCACHE | 56162306a36Sopenharmony_ci CPU_PROC_CTRL_ACP_ARCACHE, 56262306a36Sopenharmony_ci sparx5, CPU_PROC_CTRL); 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci sparx5_fdma_injection_mode(sparx5); 56562306a36Sopenharmony_ci sparx5_fdma_rx_init(sparx5, &sparx5->rx, FDMA_XTR_CHANNEL); 56662306a36Sopenharmony_ci sparx5_fdma_tx_init(sparx5, &sparx5->tx, FDMA_INJ_CHANNEL); 56762306a36Sopenharmony_ci err = sparx5_fdma_rx_alloc(sparx5); 56862306a36Sopenharmony_ci if (err) { 56962306a36Sopenharmony_ci dev_err(sparx5->dev, "Could not allocate RX buffers: %d\n", err); 57062306a36Sopenharmony_ci return err; 57162306a36Sopenharmony_ci } 57262306a36Sopenharmony_ci err = sparx5_fdma_tx_alloc(sparx5); 57362306a36Sopenharmony_ci if (err) { 57462306a36Sopenharmony_ci dev_err(sparx5->dev, "Could not allocate TX buffers: %d\n", err); 57562306a36Sopenharmony_ci return err; 57662306a36Sopenharmony_ci } 57762306a36Sopenharmony_ci return err; 57862306a36Sopenharmony_ci} 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_cistatic u32 sparx5_fdma_port_ctrl(struct sparx5 *sparx5) 58162306a36Sopenharmony_ci{ 58262306a36Sopenharmony_ci return spx5_rd(sparx5, FDMA_PORT_CTRL(0)); 58362306a36Sopenharmony_ci} 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ciint sparx5_fdma_stop(struct sparx5 *sparx5) 58662306a36Sopenharmony_ci{ 58762306a36Sopenharmony_ci u32 val; 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci napi_disable(&sparx5->rx.napi); 59062306a36Sopenharmony_ci /* Stop the fdma and channel interrupts */ 59162306a36Sopenharmony_ci sparx5_fdma_rx_deactivate(sparx5, &sparx5->rx); 59262306a36Sopenharmony_ci sparx5_fdma_tx_deactivate(sparx5, &sparx5->tx); 59362306a36Sopenharmony_ci /* Wait for the RX channel to stop */ 59462306a36Sopenharmony_ci read_poll_timeout(sparx5_fdma_port_ctrl, val, 59562306a36Sopenharmony_ci FDMA_PORT_CTRL_XTR_BUF_IS_EMPTY_GET(val) == 0, 59662306a36Sopenharmony_ci 500, 10000, 0, sparx5); 59762306a36Sopenharmony_ci return 0; 59862306a36Sopenharmony_ci} 599