blob: 3b0f277bf00102979bd6fe88add16128269be2b5 [file] [log] [blame]
/*
* 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_ */