| /* |
| * Copyright (c) 2013 Mindspeed Technologies, Inc. |
| * |
| * This program is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU General Public License |
| * as published by the Free Software Foundation; either version 2 |
| * of the License, or (at your option) any later version. |
| |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
| * |
| * |
| */ |
| |
| #ifndef _TX_H_ |
| #define _TX_H_ |
| |
| #include "system.h" |
| #include "module_qm.h" |
| #include "module_capture.h" |
| #include "mtd.h" |
| |
| #if defined(COMCERTO_2000_CLASS) |
| #define IS_QOS_ENABLED(port) (phy_port[port].flags & QOS_ENABLED) |
| #elif defined(COMCERTO_2000_UTIL) |
| #define IS_QOS_ENABLED(port) (1) /* FIXME we should have a qos enable flag in UTIL too */ |
| #endif |
| #define IS_TX_ENABLED(port) (phy_port[port].flags & TX_ENABLED) |
| |
| u32 __hif_tx_prepare(void *dmem_addr, u32 output_port, u32 *queue, u32 ctrl, u32 flags); |
| #if defined(COMCERTO_2000_CLASS) |
| u32 hif_tx_prepare(struct tMetadata *mtd); |
| #endif |
| void send_to_tx_direct(u32 port, u32 queue, void *dmem_addr, void *ddr_addr, u32 hdr_len, u16 len, u32 ctrl, u32 flags); |
| void send_to_expt_direct(void *dmem_addr, void *ddr_addr, u32 len, u32 input_port, u32 ctrl, u32 hdr_len, u32 flags); |
| void send_to_tmu(u32 port, u32 queue, void *ddr_addr, u32 len); |
| void __send_to_tx_direct(u32 port, u32 queue, void *ddr_addr, u32 len); |
| |
| |
| /** Posts a packet descriptor directly to TMU |
| * |
| * @param port TMU port |
| * @param queue TMU queue |
| * @param addr DDR class_tx_hdr + packet data address |
| * @param len DDR packet len |
| */ |
| static inline void __send_to_tmu(u32 phy, u32 queue, void *ddr_addr, u32 len) |
| { |
| tmu_tx_hdr_t tmu __attribute__ ((aligned (8))); |
| |
| tmu.pkt_ptr = (u32)ddr_addr; |
| tmu.len = len; |
| tmu.queueno = queue; |
| tmu.phyno = phy & 0xf; |
| |
| /* write buffer pointer to TMU INQ */ |
| efet_memcpy((void *)TMU_PHY_INQ_PKTPTR, &tmu, 0x8); |
| |
| PESTATUS_INCR_TX(); |
| } |
| |
| /** Retrieves hif queue |
| * |
| * @param queue priority queue |
| * @param lro lro flag |
| */ |
| static inline u32 get_hif_queue(u32 queue, u32 flags) |
| { |
| if (flags & MTD_LRO) |
| return 2; |
| else |
| return queue > 0; |
| } |
| |
| static inline u32 hif_hdr_add(void *dmem_addr, u32 client_id, u32 client_queue, u32 ctrl_le) |
| { |
| u32 hdr_len = sizeof(struct hif_pkt_hdr); |
| struct hif_pkt_hdr *hif_hdr = dmem_addr - hdr_len; |
| |
| hif_hdr->client_id = client_id; |
| hif_hdr->qNo = client_queue; |
| |
| hif_hdr->client_ctrl_le_lsw = ctrl_le >> 16; |
| hif_hdr->client_ctrl_le_msw = ctrl_le & 0xffff; |
| |
| return hdr_len; |
| } |
| |
| /*GPI may corrupt the packet if txhdr+pktlen is < GPI_TX_MIN_LENGTH */ |
| #define GPI_TX_MIN_LENGTH 73 |
| |
| /*make sure the minimal packet length passed to GEM is 16bytes to prevent any lockup */ |
| #define GEM_TX_MIN_PKT_LEN 16 |
| |
| static inline u32 tx_hdr_add(void *dmem_addr, void *ddr_addr, u16 len, u32 phy, u32 queue, u32 action) |
| { |
| class_tx_hdr_t *tx_hdr; |
| u32 hdr_len; |
| |
| tx_hdr = dmem_addr - sizeof(class_tx_hdr_t); |
| |
| if (phy < TX_PHY_TMU3) |
| { |
| if (len < GEM_TX_MIN_PKT_LEN) |
| len = GEM_TX_MIN_PKT_LEN; |
| |
| if (sizeof(class_tx_hdr_t) + len < GPI_TX_MIN_LENGTH) |
| tx_hdr = dmem_addr - (GPI_TX_MIN_LENGTH - len); |
| } |
| |
| tx_hdr = (class_tx_hdr_t *)ALIGN64(tx_hdr); |
| hdr_len = (void *)dmem_addr - (void *)tx_hdr; |
| |
| tx_hdr->start_data_off = dmem_addr - (void *)tx_hdr; |
| tx_hdr->start_buf_off = ((u32)ddr_addr - hdr_len) & 0xff; |
| tx_hdr->pkt_length = len; |
| |
| tx_hdr->act_phyno = (action & 0xf0) | (phy & 0xf); |
| tx_hdr->queueno = queue; |
| |
| return hdr_len; |
| } |
| |
| /* HGPI may not work properly for the packets size < HGPI_TX_MIN_PAYLOAD |
| * Following WA pads the tx packet length */ |
| #define HGPI_TX_MIN_PAYLOAD 60 |
| static inline u32 tx_hgpi_wa(u32 phy, void *dmem_addr, u16 *len) |
| { |
| u32 pad_len = 0; |
| |
| if (*len < HGPI_TX_MIN_PAYLOAD) |
| { |
| u32 offset = ((u32)dmem_addr & (CLASS_PBUF_SIZE - 1)) + *len; |
| |
| if (offset < CLASS_PBUF_SIZE) |
| { |
| pad_len = min(CLASS_PBUF_SIZE - offset, HGPI_TX_MIN_PAYLOAD - *len); |
| |
| memset(dmem_addr + *len, 0, pad_len); |
| } |
| |
| *len = HGPI_TX_MIN_PAYLOAD; |
| } |
| |
| return pad_len; |
| } |
| |
| #define TX_HGPI_WA(phy, mtd) do { \ |
| mtd->rx_dmem_end += tx_hgpi_wa(phy, mtd->data + mtd->offset, &mtd->length); \ |
| } while (0) |
| |
| static inline u32 __tx_prepare(u32 port, u32 *queue, u32 phy, void *dmem_addr, void *ddr_addr, u16 *len, u32 ctrl, u32 flags) |
| { |
| u32 hdr_len = 0; |
| u32 action = 0; |
| |
| if (port < GEM_PORTS) |
| { |
| if (!IS_QOS_ENABLED(port)) |
| *queue = 0; |
| |
| if (flags & MTD_TX_CHECKSUM) |
| action = ACT_TCPCHKSUM_REPLACE; |
| |
| } |
| else |
| { |
| if (IS_HIF_PORT(port)) { |
| hdr_len = __hif_tx_prepare(dmem_addr, port, queue, ctrl, flags); |
| dmem_addr -= hdr_len; |
| ddr_addr -= hdr_len; |
| *len += hdr_len; |
| |
| if (flags & MTD_LRO) |
| *queue = TMU_QUEUE_LRO; |
| } |
| |
| if (port != UTIL_PORT) |
| tx_hgpi_wa(phy, dmem_addr, len); |
| } |
| |
| hdr_len += tx_hdr_add(dmem_addr, ddr_addr, *len, phy, *queue, action); |
| |
| return hdr_len; |
| } |
| |
| #if defined(COMCERTO_2000_UTIL) |
| #define UTIL_MAX_TX_HDR_LEN 32 /* must be 64bit aligned and big enough for all tx headers class + hif/(hif+class)/(class+hif+fraghdr)) */ |
| #endif |
| #if defined(COMCERTO_2000_CLASS) |
| #define CLASS_MAX_TX_HDR_LEN 32 /* must be 64bit aligned and big enough for all tx headers class + hif/(hif+ipsec)/(hif+lro)/util) */ |
| static inline u32 tx_prepare(struct tMetadata *mtd, void *dmem_addr, void *ddr_addr) |
| { |
| u32 hdr_len = 0; |
| u32 phy = get_tx_phy(mtd->output_port); |
| u32 action = 0; |
| |
| if (mtd->output_port < GEM_PORTS) |
| { |
| #if defined(CFG_PCAP) |
| if (M_pktcap_chk_enable(mtd->output_port)) |
| M_pktcap_process(mtd, mtd->output_port); |
| #endif |
| |
| if (!IS_QOS_ENABLED(mtd->output_port)) |
| mtd->queue = 0; |
| |
| if (mtd->flags & MTD_TX_CHECKSUM) |
| action = ACT_TCPCHKSUM_REPLACE; |
| } |
| else |
| { |
| if (IS_HIF_PORT(mtd->output_port)) { |
| hdr_len += hif_tx_prepare(mtd); |
| mtd->offset -= hdr_len; |
| mtd->length += hdr_len; |
| |
| if (mtd->flags & MTD_LRO) |
| mtd->queue = TMU_QUEUE_LRO; |
| } |
| |
| if (mtd->output_port != UTIL_PORT) |
| TX_HGPI_WA(phy, mtd); |
| } |
| |
| hdr_len += tx_hdr_add(dmem_addr - hdr_len, ddr_addr - hdr_len, mtd->length, phy, mtd->queue, action); |
| |
| return hdr_len; |
| } |
| #endif |
| |
| #endif /* _TX_H_ */ |