823 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			823 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| // SPDX-License-Identifier: GPL-2.0-or-later
 | |
| /* Applied Micro X-Gene SoC Ethernet Classifier structures
 | |
|  *
 | |
|  * Copyright (c) 2016, Applied Micro Circuits Corporation
 | |
|  * Authors: Khuong Dinh <kdinh@apm.com>
 | |
|  *          Tanmay Inamdar <tinamdar@apm.com>
 | |
|  *          Iyappan Subramanian <isubramanian@apm.com>
 | |
|  */
 | |
| 
 | |
| #include "xgene_enet_main.h"
 | |
| 
 | |
| /* interfaces to convert structures to HW recognized bit formats */
 | |
| static void xgene_cle_sband_to_hw(u8 frag, enum xgene_cle_prot_version ver,
 | |
| 				  enum xgene_cle_prot_type type, u32 len,
 | |
| 				  u32 *reg)
 | |
| {
 | |
| 	*reg =  SET_VAL(SB_IPFRAG, frag) |
 | |
| 		SET_VAL(SB_IPPROT, type) |
 | |
| 		SET_VAL(SB_IPVER, ver) |
 | |
| 		SET_VAL(SB_HDRLEN, len);
 | |
| }
 | |
| 
 | |
| static void xgene_cle_idt_to_hw(struct xgene_enet_pdata *pdata,
 | |
| 				u32 dstqid, u32 fpsel,
 | |
| 				u32 nfpsel, u32 *idt_reg)
 | |
| {
 | |
| 	if (pdata->enet_id == XGENE_ENET1) {
 | |
| 		*idt_reg = SET_VAL(IDT_DSTQID, dstqid) |
 | |
| 			   SET_VAL(IDT_FPSEL1, fpsel)  |
 | |
| 			   SET_VAL(IDT_NFPSEL1, nfpsel);
 | |
| 	} else {
 | |
| 		*idt_reg = SET_VAL(IDT_DSTQID, dstqid) |
 | |
| 			   SET_VAL(IDT_FPSEL, fpsel)   |
 | |
| 			   SET_VAL(IDT_NFPSEL, nfpsel);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static void xgene_cle_dbptr_to_hw(struct xgene_enet_pdata *pdata,
 | |
| 				  struct xgene_cle_dbptr *dbptr, u32 *buf)
 | |
| {
 | |
| 	buf[0] = SET_VAL(CLE_DROP, dbptr->drop);
 | |
| 	buf[4] = SET_VAL(CLE_FPSEL, dbptr->fpsel) |
 | |
| 		 SET_VAL(CLE_NFPSEL, dbptr->nxtfpsel) |
 | |
| 		 SET_VAL(CLE_DSTQIDL, dbptr->dstqid);
 | |
| 
 | |
| 	buf[5] = SET_VAL(CLE_DSTQIDH, (u32)dbptr->dstqid >> CLE_DSTQIDL_LEN) |
 | |
| 		 SET_VAL(CLE_PRIORITY, dbptr->cle_priority);
 | |
| }
 | |
| 
 | |
| static void xgene_cle_kn_to_hw(struct xgene_cle_ptree_kn *kn, u32 *buf)
 | |
| {
 | |
| 	u32 i, j = 0;
 | |
| 	u32 data;
 | |
| 
 | |
| 	buf[j++] = SET_VAL(CLE_TYPE, kn->node_type);
 | |
| 	for (i = 0; i < kn->num_keys; i++) {
 | |
| 		struct xgene_cle_ptree_key *key = &kn->key[i];
 | |
| 
 | |
| 		if (!(i % 2)) {
 | |
| 			buf[j] = SET_VAL(CLE_KN_PRIO, key->priority) |
 | |
| 				 SET_VAL(CLE_KN_RPTR, key->result_pointer);
 | |
| 		} else {
 | |
| 			data = SET_VAL(CLE_KN_PRIO, key->priority) |
 | |
| 			       SET_VAL(CLE_KN_RPTR, key->result_pointer);
 | |
| 			buf[j++] |= (data << 16);
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static void xgene_cle_dn_to_hw(const struct xgene_cle_ptree_ewdn *dn,
 | |
| 			       u32 *buf, u32 jb)
 | |
| {
 | |
| 	const struct xgene_cle_ptree_branch *br;
 | |
| 	u32 i, j = 0;
 | |
| 	u32 npp;
 | |
| 
 | |
| 	buf[j++] = SET_VAL(CLE_DN_TYPE, dn->node_type) |
 | |
| 		   SET_VAL(CLE_DN_LASTN, dn->last_node) |
 | |
| 		   SET_VAL(CLE_DN_HLS, dn->hdr_len_store) |
 | |
| 		   SET_VAL(CLE_DN_EXT, dn->hdr_extn) |
 | |
| 		   SET_VAL(CLE_DN_BSTOR, dn->byte_store) |
 | |
| 		   SET_VAL(CLE_DN_SBSTOR, dn->search_byte_store) |
 | |
| 		   SET_VAL(CLE_DN_RPTR, dn->result_pointer);
 | |
| 
 | |
| 	for (i = 0; i < dn->num_branches; i++) {
 | |
| 		br = &dn->branch[i];
 | |
| 		npp = br->next_packet_pointer;
 | |
| 
 | |
| 		if ((br->jump_rel == JMP_ABS) && (npp < CLE_PKTRAM_SIZE))
 | |
| 			npp += jb;
 | |
| 
 | |
| 		buf[j++] = SET_VAL(CLE_BR_VALID, br->valid) |
 | |
| 			   SET_VAL(CLE_BR_NPPTR, npp) |
 | |
| 			   SET_VAL(CLE_BR_JB, br->jump_bw) |
 | |
| 			   SET_VAL(CLE_BR_JR, br->jump_rel) |
 | |
| 			   SET_VAL(CLE_BR_OP, br->operation) |
 | |
| 			   SET_VAL(CLE_BR_NNODE, br->next_node) |
 | |
| 			   SET_VAL(CLE_BR_NBR, br->next_branch);
 | |
| 
 | |
| 		buf[j++] = SET_VAL(CLE_BR_DATA, br->data) |
 | |
| 			   SET_VAL(CLE_BR_MASK, br->mask);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static int xgene_cle_poll_cmd_done(void __iomem *base,
 | |
| 				   enum xgene_cle_cmd_type cmd)
 | |
| {
 | |
| 	u32 status, loop = 10;
 | |
| 	int ret = -EBUSY;
 | |
| 
 | |
| 	while (loop--) {
 | |
| 		status = ioread32(base + INDCMD_STATUS);
 | |
| 		if (status & cmd) {
 | |
| 			ret = 0;
 | |
| 			break;
 | |
| 		}
 | |
| 		usleep_range(1000, 2000);
 | |
| 	}
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static int xgene_cle_dram_wr(struct xgene_enet_cle *cle, u32 *data, u8 nregs,
 | |
| 			     u32 index, enum xgene_cle_dram_type type,
 | |
| 			     enum xgene_cle_cmd_type cmd)
 | |
| {
 | |
| 	enum xgene_cle_parser parser = cle->active_parser;
 | |
| 	void __iomem *base = cle->base;
 | |
| 	u32 i, j, ind_addr;
 | |
| 	u8 port, nparsers;
 | |
| 	int ret = 0;
 | |
| 
 | |
| 	/* PTREE_RAM onwards, DRAM regions are common for all parsers */
 | |
| 	nparsers = (type >= PTREE_RAM) ? 1 : cle->parsers;
 | |
| 
 | |
| 	for (i = 0; i < nparsers; i++) {
 | |
| 		port = i;
 | |
| 		if ((type < PTREE_RAM) && (parser != PARSER_ALL))
 | |
| 			port = parser;
 | |
| 
 | |
| 		ind_addr = XGENE_CLE_DRAM(type + (port * 4)) | index;
 | |
| 		iowrite32(ind_addr, base + INDADDR);
 | |
| 		for (j = 0; j < nregs; j++)
 | |
| 			iowrite32(data[j], base + DATA_RAM0 + (j * 4));
 | |
| 		iowrite32(cmd, base + INDCMD);
 | |
| 
 | |
| 		ret = xgene_cle_poll_cmd_done(base, cmd);
 | |
| 		if (ret)
 | |
| 			break;
 | |
| 	}
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static void xgene_cle_enable_ptree(struct xgene_enet_pdata *pdata,
 | |
| 				   struct xgene_enet_cle *cle)
 | |
| {
 | |
| 	struct xgene_cle_ptree *ptree = &cle->ptree;
 | |
| 	void __iomem *addr, *base = cle->base;
 | |
| 	u32 offset = CLE_PORT_OFFSET;
 | |
| 	u32 i;
 | |
| 
 | |
| 	/* 1G port has to advance 4 bytes and 10G has to advance 8 bytes */
 | |
| 	ptree->start_pkt += cle->jump_bytes;
 | |
| 	for (i = 0; i < cle->parsers; i++) {
 | |
| 		if (cle->active_parser != PARSER_ALL)
 | |
| 			addr = base + cle->active_parser * offset;
 | |
| 		else
 | |
| 			addr = base + (i * offset);
 | |
| 
 | |
| 		iowrite32(ptree->start_node & 0x3fff, addr + SNPTR0);
 | |
| 		iowrite32(ptree->start_pkt & 0x1ff, addr + SPPTR0);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static int xgene_cle_setup_dbptr(struct xgene_enet_pdata *pdata,
 | |
| 				 struct xgene_enet_cle *cle)
 | |
| {
 | |
| 	struct xgene_cle_ptree *ptree = &cle->ptree;
 | |
| 	u32 buf[CLE_DRAM_REGS];
 | |
| 	u32 i;
 | |
| 	int ret;
 | |
| 
 | |
| 	memset(buf, 0, sizeof(buf));
 | |
| 	for (i = 0; i < ptree->num_dbptr; i++) {
 | |
| 		xgene_cle_dbptr_to_hw(pdata, &ptree->dbptr[i], buf);
 | |
| 		ret = xgene_cle_dram_wr(cle, buf, 6, i + ptree->start_dbptr,
 | |
| 					DB_RAM,	CLE_CMD_WR);
 | |
| 		if (ret)
 | |
| 			return ret;
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static const struct xgene_cle_ptree_ewdn xgene_init_ptree_dn[] = {
 | |
| 	{
 | |
| 		/* PKT_TYPE_NODE */
 | |
| 		.node_type = EWDN,
 | |
| 		.last_node = 0,
 | |
| 		.hdr_len_store = 1,
 | |
| 		.hdr_extn = NO_BYTE,
 | |
| 		.byte_store = NO_BYTE,
 | |
| 		.search_byte_store = NO_BYTE,
 | |
| 		.result_pointer = DB_RES_DROP,
 | |
| 		.num_branches = 2,
 | |
| 		.branch = {
 | |
| 			{
 | |
| 				/* IPV4 */
 | |
| 				.valid = 1,
 | |
| 				.next_packet_pointer = 22,
 | |
| 				.jump_bw = JMP_FW,
 | |
| 				.jump_rel = JMP_ABS,
 | |
| 				.operation = EQT,
 | |
| 				.next_node = PKT_PROT_NODE,
 | |
| 				.next_branch = 0,
 | |
| 				.data = 0x8,
 | |
| 				.mask = 0x0
 | |
| 			},
 | |
| 			{
 | |
| 				.valid = 0,
 | |
| 				.next_packet_pointer = 262,
 | |
| 				.jump_bw = JMP_FW,
 | |
| 				.jump_rel = JMP_ABS,
 | |
| 				.operation = EQT,
 | |
| 				.next_node = LAST_NODE,
 | |
| 				.next_branch = 0,
 | |
| 				.data = 0x0,
 | |
| 				.mask = 0xffff
 | |
| 			}
 | |
| 		},
 | |
| 	},
 | |
| 	{
 | |
| 		/* PKT_PROT_NODE */
 | |
| 		.node_type = EWDN,
 | |
| 		.last_node = 0,
 | |
| 		.hdr_len_store = 1,
 | |
| 		.hdr_extn = NO_BYTE,
 | |
| 		.byte_store = NO_BYTE,
 | |
| 		.search_byte_store = NO_BYTE,
 | |
| 		.result_pointer = DB_RES_DROP,
 | |
| 		.num_branches = 3,
 | |
| 		.branch = {
 | |
| 			{
 | |
| 				/* TCP */
 | |
| 				.valid = 1,
 | |
| 				.next_packet_pointer = 26,
 | |
| 				.jump_bw = JMP_FW,
 | |
| 				.jump_rel = JMP_ABS,
 | |
| 				.operation = EQT,
 | |
| 				.next_node = RSS_IPV4_TCP_NODE,
 | |
| 				.next_branch = 0,
 | |
| 				.data = 0x0600,
 | |
| 				.mask = 0x00ff
 | |
| 			},
 | |
| 			{
 | |
| 				/* UDP */
 | |
| 				.valid = 1,
 | |
| 				.next_packet_pointer = 26,
 | |
| 				.jump_bw = JMP_FW,
 | |
| 				.jump_rel = JMP_ABS,
 | |
| 				.operation = EQT,
 | |
| 				.next_node = RSS_IPV4_UDP_NODE,
 | |
| 				.next_branch = 0,
 | |
| 				.data = 0x1100,
 | |
| 				.mask = 0x00ff
 | |
| 			},
 | |
| 			{
 | |
| 				.valid = 0,
 | |
| 				.next_packet_pointer = 26,
 | |
| 				.jump_bw = JMP_FW,
 | |
| 				.jump_rel = JMP_ABS,
 | |
| 				.operation = EQT,
 | |
| 				.next_node = RSS_IPV4_OTHERS_NODE,
 | |
| 				.next_branch = 0,
 | |
| 				.data = 0x0,
 | |
| 				.mask = 0xffff
 | |
| 			}
 | |
| 		}
 | |
| 	},
 | |
| 	{
 | |
| 		/* RSS_IPV4_TCP_NODE */
 | |
| 		.node_type = EWDN,
 | |
| 		.last_node = 0,
 | |
| 		.hdr_len_store = 1,
 | |
| 		.hdr_extn = NO_BYTE,
 | |
| 		.byte_store = NO_BYTE,
 | |
| 		.search_byte_store = BOTH_BYTES,
 | |
| 		.result_pointer = DB_RES_DROP,
 | |
| 		.num_branches = 6,
 | |
| 		.branch = {
 | |
| 			{
 | |
| 				/* SRC IPV4 B01 */
 | |
| 				.valid = 0,
 | |
| 				.next_packet_pointer = 28,
 | |
| 				.jump_bw = JMP_FW,
 | |
| 				.jump_rel = JMP_ABS,
 | |
| 				.operation = EQT,
 | |
| 				.next_node = RSS_IPV4_TCP_NODE,
 | |
| 				.next_branch = 1,
 | |
| 				.data = 0x0,
 | |
| 				.mask = 0xffff
 | |
| 			},
 | |
| 			{
 | |
| 				/* SRC IPV4 B23 */
 | |
| 				.valid = 0,
 | |
| 				.next_packet_pointer = 30,
 | |
| 				.jump_bw = JMP_FW,
 | |
| 				.jump_rel = JMP_ABS,
 | |
| 				.operation = EQT,
 | |
| 				.next_node = RSS_IPV4_TCP_NODE,
 | |
| 				.next_branch = 2,
 | |
| 				.data = 0x0,
 | |
| 				.mask = 0xffff
 | |
| 			},
 | |
| 			{
 | |
| 				/* DST IPV4 B01 */
 | |
| 				.valid = 0,
 | |
| 				.next_packet_pointer = 32,
 | |
| 				.jump_bw = JMP_FW,
 | |
| 				.jump_rel = JMP_ABS,
 | |
| 				.operation = EQT,
 | |
| 				.next_node = RSS_IPV4_TCP_NODE,
 | |
| 				.next_branch = 3,
 | |
| 				.data = 0x0,
 | |
| 				.mask = 0xffff
 | |
| 			},
 | |
| 			{
 | |
| 				/* DST IPV4 B23 */
 | |
| 				.valid = 0,
 | |
| 				.next_packet_pointer = 34,
 | |
| 				.jump_bw = JMP_FW,
 | |
| 				.jump_rel = JMP_ABS,
 | |
| 				.operation = EQT,
 | |
| 				.next_node = RSS_IPV4_TCP_NODE,
 | |
| 				.next_branch = 4,
 | |
| 				.data = 0x0,
 | |
| 				.mask = 0xffff
 | |
| 			},
 | |
| 			{
 | |
| 				/* TCP SRC Port */
 | |
| 				.valid = 0,
 | |
| 				.next_packet_pointer = 36,
 | |
| 				.jump_bw = JMP_FW,
 | |
| 				.jump_rel = JMP_ABS,
 | |
| 				.operation = EQT,
 | |
| 				.next_node = RSS_IPV4_TCP_NODE,
 | |
| 				.next_branch = 5,
 | |
| 				.data = 0x0,
 | |
| 				.mask = 0xffff
 | |
| 			},
 | |
| 			{
 | |
| 				/* TCP DST Port */
 | |
| 				.valid = 0,
 | |
| 				.next_packet_pointer = 256,
 | |
| 				.jump_bw = JMP_FW,
 | |
| 				.jump_rel = JMP_ABS,
 | |
| 				.operation = EQT,
 | |
| 				.next_node = LAST_NODE,
 | |
| 				.next_branch = 0,
 | |
| 				.data = 0x0,
 | |
| 				.mask = 0xffff
 | |
| 			}
 | |
| 		}
 | |
| 	},
 | |
| 	{
 | |
| 		/* RSS_IPV4_UDP_NODE */
 | |
| 		.node_type = EWDN,
 | |
| 		.last_node = 0,
 | |
| 		.hdr_len_store = 1,
 | |
| 		.hdr_extn = NO_BYTE,
 | |
| 		.byte_store = NO_BYTE,
 | |
| 		.search_byte_store = BOTH_BYTES,
 | |
| 		.result_pointer = DB_RES_DROP,
 | |
| 		.num_branches = 6,
 | |
| 		.branch = {
 | |
| 			{
 | |
| 				/* SRC IPV4 B01 */
 | |
| 				.valid = 0,
 | |
| 				.next_packet_pointer = 28,
 | |
| 				.jump_bw = JMP_FW,
 | |
| 				.jump_rel = JMP_ABS,
 | |
| 				.operation = EQT,
 | |
| 				.next_node = RSS_IPV4_UDP_NODE,
 | |
| 				.next_branch = 1,
 | |
| 				.data = 0x0,
 | |
| 				.mask = 0xffff
 | |
| 			},
 | |
| 			{
 | |
| 				/* SRC IPV4 B23 */
 | |
| 				.valid = 0,
 | |
| 				.next_packet_pointer = 30,
 | |
| 				.jump_bw = JMP_FW,
 | |
| 				.jump_rel = JMP_ABS,
 | |
| 				.operation = EQT,
 | |
| 				.next_node = RSS_IPV4_UDP_NODE,
 | |
| 				.next_branch = 2,
 | |
| 				.data = 0x0,
 | |
| 				.mask = 0xffff
 | |
| 			},
 | |
| 			{
 | |
| 				/* DST IPV4 B01 */
 | |
| 				.valid = 0,
 | |
| 				.next_packet_pointer = 32,
 | |
| 				.jump_bw = JMP_FW,
 | |
| 				.jump_rel = JMP_ABS,
 | |
| 				.operation = EQT,
 | |
| 				.next_node = RSS_IPV4_UDP_NODE,
 | |
| 				.next_branch = 3,
 | |
| 				.data = 0x0,
 | |
| 				.mask = 0xffff
 | |
| 			},
 | |
| 			{
 | |
| 				/* DST IPV4 B23 */
 | |
| 				.valid = 0,
 | |
| 				.next_packet_pointer = 34,
 | |
| 				.jump_bw = JMP_FW,
 | |
| 				.jump_rel = JMP_ABS,
 | |
| 				.operation = EQT,
 | |
| 				.next_node = RSS_IPV4_UDP_NODE,
 | |
| 				.next_branch = 4,
 | |
| 				.data = 0x0,
 | |
| 				.mask = 0xffff
 | |
| 			},
 | |
| 			{
 | |
| 				/* TCP SRC Port */
 | |
| 				.valid = 0,
 | |
| 				.next_packet_pointer = 36,
 | |
| 				.jump_bw = JMP_FW,
 | |
| 				.jump_rel = JMP_ABS,
 | |
| 				.operation = EQT,
 | |
| 				.next_node = RSS_IPV4_UDP_NODE,
 | |
| 				.next_branch = 5,
 | |
| 				.data = 0x0,
 | |
| 				.mask = 0xffff
 | |
| 			},
 | |
| 			{
 | |
| 				/* TCP DST Port */
 | |
| 				.valid = 0,
 | |
| 				.next_packet_pointer = 258,
 | |
| 				.jump_bw = JMP_FW,
 | |
| 				.jump_rel = JMP_ABS,
 | |
| 				.operation = EQT,
 | |
| 				.next_node = LAST_NODE,
 | |
| 				.next_branch = 0,
 | |
| 				.data = 0x0,
 | |
| 				.mask = 0xffff
 | |
| 			}
 | |
| 		}
 | |
| 	},
 | |
| 	{
 | |
| 		/* RSS_IPV4_OTHERS_NODE */
 | |
| 		.node_type = EWDN,
 | |
| 		.last_node = 0,
 | |
| 		.hdr_len_store = 1,
 | |
| 		.hdr_extn = NO_BYTE,
 | |
| 		.byte_store = NO_BYTE,
 | |
| 		.search_byte_store = BOTH_BYTES,
 | |
| 		.result_pointer = DB_RES_DROP,
 | |
| 		.num_branches = 6,
 | |
| 		.branch = {
 | |
| 			{
 | |
| 				/* SRC IPV4 B01 */
 | |
| 				.valid = 0,
 | |
| 				.next_packet_pointer = 28,
 | |
| 				.jump_bw = JMP_FW,
 | |
| 				.jump_rel = JMP_ABS,
 | |
| 				.operation = EQT,
 | |
| 				.next_node = RSS_IPV4_OTHERS_NODE,
 | |
| 				.next_branch = 1,
 | |
| 				.data = 0x0,
 | |
| 				.mask = 0xffff
 | |
| 			},
 | |
| 			{
 | |
| 				/* SRC IPV4 B23 */
 | |
| 				.valid = 0,
 | |
| 				.next_packet_pointer = 30,
 | |
| 				.jump_bw = JMP_FW,
 | |
| 				.jump_rel = JMP_ABS,
 | |
| 				.operation = EQT,
 | |
| 				.next_node = RSS_IPV4_OTHERS_NODE,
 | |
| 				.next_branch = 2,
 | |
| 				.data = 0x0,
 | |
| 				.mask = 0xffff
 | |
| 			},
 | |
| 			{
 | |
| 				/* DST IPV4 B01 */
 | |
| 				.valid = 0,
 | |
| 				.next_packet_pointer = 32,
 | |
| 				.jump_bw = JMP_FW,
 | |
| 				.jump_rel = JMP_ABS,
 | |
| 				.operation = EQT,
 | |
| 				.next_node = RSS_IPV4_OTHERS_NODE,
 | |
| 				.next_branch = 3,
 | |
| 				.data = 0x0,
 | |
| 				.mask = 0xffff
 | |
| 			},
 | |
| 			{
 | |
| 				/* DST IPV4 B23 */
 | |
| 				.valid = 0,
 | |
| 				.next_packet_pointer = 34,
 | |
| 				.jump_bw = JMP_FW,
 | |
| 				.jump_rel = JMP_ABS,
 | |
| 				.operation = EQT,
 | |
| 				.next_node = RSS_IPV4_OTHERS_NODE,
 | |
| 				.next_branch = 4,
 | |
| 				.data = 0x0,
 | |
| 				.mask = 0xffff
 | |
| 			},
 | |
| 			{
 | |
| 				/* TCP SRC Port */
 | |
| 				.valid = 0,
 | |
| 				.next_packet_pointer = 36,
 | |
| 				.jump_bw = JMP_FW,
 | |
| 				.jump_rel = JMP_ABS,
 | |
| 				.operation = EQT,
 | |
| 				.next_node = RSS_IPV4_OTHERS_NODE,
 | |
| 				.next_branch = 5,
 | |
| 				.data = 0x0,
 | |
| 				.mask = 0xffff
 | |
| 			},
 | |
| 			{
 | |
| 				/* TCP DST Port */
 | |
| 				.valid = 0,
 | |
| 				.next_packet_pointer = 260,
 | |
| 				.jump_bw = JMP_FW,
 | |
| 				.jump_rel = JMP_ABS,
 | |
| 				.operation = EQT,
 | |
| 				.next_node = LAST_NODE,
 | |
| 				.next_branch = 0,
 | |
| 				.data = 0x0,
 | |
| 				.mask = 0xffff
 | |
| 			}
 | |
| 		}
 | |
| 	},
 | |
| 
 | |
| 	{
 | |
| 		/* LAST NODE */
 | |
| 		.node_type = EWDN,
 | |
| 		.last_node = 1,
 | |
| 		.hdr_len_store = 1,
 | |
| 		.hdr_extn = NO_BYTE,
 | |
| 		.byte_store = NO_BYTE,
 | |
| 		.search_byte_store = NO_BYTE,
 | |
| 		.result_pointer = DB_RES_DROP,
 | |
| 		.num_branches = 1,
 | |
| 		.branch = {
 | |
| 			{
 | |
| 				.valid = 0,
 | |
| 				.next_packet_pointer = 0,
 | |
| 				.jump_bw = JMP_FW,
 | |
| 				.jump_rel = JMP_ABS,
 | |
| 				.operation = EQT,
 | |
| 				.next_node = MAX_NODES,
 | |
| 				.next_branch = 0,
 | |
| 				.data = 0,
 | |
| 				.mask = 0xffff
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| };
 | |
| 
 | |
| static int xgene_cle_setup_node(struct xgene_enet_pdata *pdata,
 | |
| 				struct xgene_enet_cle *cle)
 | |
| {
 | |
| 	struct xgene_cle_ptree *ptree = &cle->ptree;
 | |
| 	const struct xgene_cle_ptree_ewdn *dn = xgene_init_ptree_dn;
 | |
| 	int num_dn = ARRAY_SIZE(xgene_init_ptree_dn);
 | |
| 	struct xgene_cle_ptree_kn *kn = ptree->kn;
 | |
| 	u32 buf[CLE_DRAM_REGS];
 | |
| 	int i, j, ret;
 | |
| 
 | |
| 	memset(buf, 0, sizeof(buf));
 | |
| 	for (i = 0; i < num_dn; i++) {
 | |
| 		xgene_cle_dn_to_hw(&dn[i], buf, cle->jump_bytes);
 | |
| 		ret = xgene_cle_dram_wr(cle, buf, 17, i + ptree->start_node,
 | |
| 					PTREE_RAM, CLE_CMD_WR);
 | |
| 		if (ret)
 | |
| 			return ret;
 | |
| 	}
 | |
| 
 | |
| 	/* continue node index for key node */
 | |
| 	memset(buf, 0, sizeof(buf));
 | |
| 	for (j = i; j < (ptree->num_kn + num_dn); j++) {
 | |
| 		xgene_cle_kn_to_hw(&kn[j - num_dn], buf);
 | |
| 		ret = xgene_cle_dram_wr(cle, buf, 17, j + ptree->start_node,
 | |
| 					PTREE_RAM, CLE_CMD_WR);
 | |
| 		if (ret)
 | |
| 			return ret;
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int xgene_cle_setup_ptree(struct xgene_enet_pdata *pdata,
 | |
| 				 struct xgene_enet_cle *cle)
 | |
| {
 | |
| 	int ret;
 | |
| 
 | |
| 	ret = xgene_cle_setup_node(pdata, cle);
 | |
| 	if (ret)
 | |
| 		return ret;
 | |
| 
 | |
| 	ret = xgene_cle_setup_dbptr(pdata, cle);
 | |
| 	if (ret)
 | |
| 		return ret;
 | |
| 
 | |
| 	xgene_cle_enable_ptree(pdata, cle);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static void xgene_cle_setup_def_dbptr(struct xgene_enet_pdata *pdata,
 | |
| 				      struct xgene_enet_cle *enet_cle,
 | |
| 				      struct xgene_cle_dbptr *dbptr,
 | |
| 				      u32 index, u8 priority)
 | |
| {
 | |
| 	void __iomem *base = enet_cle->base;
 | |
| 	void __iomem *base_addr;
 | |
| 	u32 buf[CLE_DRAM_REGS];
 | |
| 	u32 def_cls, offset;
 | |
| 	u32 i, j;
 | |
| 
 | |
| 	memset(buf, 0, sizeof(buf));
 | |
| 	xgene_cle_dbptr_to_hw(pdata, dbptr, buf);
 | |
| 
 | |
| 	for (i = 0; i < enet_cle->parsers; i++) {
 | |
| 		if (enet_cle->active_parser != PARSER_ALL) {
 | |
| 			offset = enet_cle->active_parser *
 | |
| 				CLE_PORT_OFFSET;
 | |
| 		} else {
 | |
| 			offset = i * CLE_PORT_OFFSET;
 | |
| 		}
 | |
| 
 | |
| 		base_addr = base + DFCLSRESDB00 + offset;
 | |
| 		for (j = 0; j < 6; j++)
 | |
| 			iowrite32(buf[j], base_addr + (j * 4));
 | |
| 
 | |
| 		def_cls = ((priority & 0x7) << 10) | (index & 0x3ff);
 | |
| 		iowrite32(def_cls, base + DFCLSRESDBPTR0 + offset);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static int xgene_cle_set_rss_sband(struct xgene_enet_cle *cle)
 | |
| {
 | |
| 	u32 idx = CLE_PKTRAM_SIZE / sizeof(u32);
 | |
| 	u32 mac_hdr_len = ETH_HLEN;
 | |
| 	u32 sband, reg = 0;
 | |
| 	u32 ipv4_ihl = 5;
 | |
| 	u32 hdr_len;
 | |
| 	int ret;
 | |
| 
 | |
| 	/* Sideband: IPV4/TCP packets */
 | |
| 	hdr_len = (mac_hdr_len << 5) | ipv4_ihl;
 | |
| 	xgene_cle_sband_to_hw(0, XGENE_CLE_IPV4, XGENE_CLE_TCP, hdr_len, ®);
 | |
| 	sband = reg;
 | |
| 
 | |
| 	/* Sideband: IPv4/UDP packets */
 | |
| 	hdr_len = (mac_hdr_len << 5) | ipv4_ihl;
 | |
| 	xgene_cle_sband_to_hw(1, XGENE_CLE_IPV4, XGENE_CLE_UDP, hdr_len, ®);
 | |
| 	sband |= (reg << 16);
 | |
| 
 | |
| 	ret = xgene_cle_dram_wr(cle, &sband, 1, idx, PKT_RAM, CLE_CMD_WR);
 | |
| 	if (ret)
 | |
| 		return ret;
 | |
| 
 | |
| 	/* Sideband: IPv4/RAW packets */
 | |
| 	hdr_len = (mac_hdr_len << 5) | ipv4_ihl;
 | |
| 	xgene_cle_sband_to_hw(0, XGENE_CLE_IPV4, XGENE_CLE_OTHER,
 | |
| 			      hdr_len, ®);
 | |
| 	sband = reg;
 | |
| 
 | |
| 	/* Sideband: Ethernet II/RAW packets */
 | |
| 	hdr_len = (mac_hdr_len << 5);
 | |
| 	xgene_cle_sband_to_hw(0, XGENE_CLE_IPV4, XGENE_CLE_OTHER,
 | |
| 			      hdr_len, ®);
 | |
| 	sband |= (reg << 16);
 | |
| 
 | |
| 	ret = xgene_cle_dram_wr(cle, &sband, 1, idx + 1, PKT_RAM, CLE_CMD_WR);
 | |
| 	if (ret)
 | |
| 		return ret;
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int xgene_cle_set_rss_skeys(struct xgene_enet_cle *cle)
 | |
| {
 | |
| 	u32 secret_key_ipv4[4];  /* 16 Bytes*/
 | |
| 	int ret = 0;
 | |
| 
 | |
| 	get_random_bytes(secret_key_ipv4, 16);
 | |
| 	ret = xgene_cle_dram_wr(cle, secret_key_ipv4, 4, 0,
 | |
| 				RSS_IPV4_HASH_SKEY, CLE_CMD_WR);
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static int xgene_cle_set_rss_idt(struct xgene_enet_pdata *pdata)
 | |
| {
 | |
| 	u32 fpsel, dstqid, nfpsel, idt_reg, idx;
 | |
| 	int i, ret = 0;
 | |
| 	u16 pool_id;
 | |
| 
 | |
| 	for (i = 0; i < XGENE_CLE_IDT_ENTRIES; i++) {
 | |
| 		idx = i % pdata->rxq_cnt;
 | |
| 		pool_id = pdata->rx_ring[idx]->buf_pool->id;
 | |
| 		fpsel = xgene_enet_get_fpsel(pool_id);
 | |
| 		dstqid = xgene_enet_dst_ring_num(pdata->rx_ring[idx]);
 | |
| 		nfpsel = 0;
 | |
| 		if (pdata->rx_ring[idx]->page_pool) {
 | |
| 			pool_id = pdata->rx_ring[idx]->page_pool->id;
 | |
| 			nfpsel = xgene_enet_get_fpsel(pool_id);
 | |
| 		}
 | |
| 
 | |
| 		idt_reg = 0;
 | |
| 		xgene_cle_idt_to_hw(pdata, dstqid, fpsel, nfpsel, &idt_reg);
 | |
| 		ret = xgene_cle_dram_wr(&pdata->cle, &idt_reg, 1, i,
 | |
| 					RSS_IDT, CLE_CMD_WR);
 | |
| 		if (ret)
 | |
| 			return ret;
 | |
| 	}
 | |
| 
 | |
| 	ret = xgene_cle_set_rss_skeys(&pdata->cle);
 | |
| 	if (ret)
 | |
| 		return ret;
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int xgene_cle_setup_rss(struct xgene_enet_pdata *pdata)
 | |
| {
 | |
| 	struct xgene_enet_cle *cle = &pdata->cle;
 | |
| 	void __iomem *base = cle->base;
 | |
| 	u32 offset, val = 0;
 | |
| 	int i, ret = 0;
 | |
| 
 | |
| 	offset = CLE_PORT_OFFSET;
 | |
| 	for (i = 0; i < cle->parsers; i++) {
 | |
| 		if (cle->active_parser != PARSER_ALL)
 | |
| 			offset = cle->active_parser * CLE_PORT_OFFSET;
 | |
| 		else
 | |
| 			offset = i * CLE_PORT_OFFSET;
 | |
| 
 | |
| 		/* enable RSS */
 | |
| 		val = (RSS_IPV4_12B << 1) | 0x1;
 | |
| 		writel(val, base + RSS_CTRL0 + offset);
 | |
| 	}
 | |
| 
 | |
| 	/* setup sideband data */
 | |
| 	ret = xgene_cle_set_rss_sband(cle);
 | |
| 	if (ret)
 | |
| 		return ret;
 | |
| 
 | |
| 	/* setup indirection table */
 | |
| 	ret = xgene_cle_set_rss_idt(pdata);
 | |
| 	if (ret)
 | |
| 		return ret;
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int xgene_enet_cle_init(struct xgene_enet_pdata *pdata)
 | |
| {
 | |
| 	struct xgene_enet_cle *enet_cle = &pdata->cle;
 | |
| 	u32 def_qid, def_fpsel, def_nxtfpsel, pool_id;
 | |
| 	struct xgene_cle_dbptr dbptr[DB_MAX_PTRS];
 | |
| 	struct xgene_cle_ptree *ptree;
 | |
| 	struct xgene_cle_ptree_kn kn;
 | |
| 	int ret;
 | |
| 
 | |
| 	if (pdata->phy_mode != PHY_INTERFACE_MODE_XGMII)
 | |
| 		return -EINVAL;
 | |
| 
 | |
| 	ptree = &enet_cle->ptree;
 | |
| 	ptree->start_pkt = 12; /* Ethertype */
 | |
| 
 | |
| 	ret = xgene_cle_setup_rss(pdata);
 | |
| 	if (ret) {
 | |
| 		netdev_err(pdata->ndev, "RSS initialization failed\n");
 | |
| 		return ret;
 | |
| 	}
 | |
| 
 | |
| 	def_qid = xgene_enet_dst_ring_num(pdata->rx_ring[0]);
 | |
| 	pool_id = pdata->rx_ring[0]->buf_pool->id;
 | |
| 	def_fpsel = xgene_enet_get_fpsel(pool_id);
 | |
| 	def_nxtfpsel = 0;
 | |
| 	if (pdata->rx_ring[0]->page_pool) {
 | |
| 		pool_id = pdata->rx_ring[0]->page_pool->id;
 | |
| 		def_nxtfpsel = xgene_enet_get_fpsel(pool_id);
 | |
| 	}
 | |
| 
 | |
| 	memset(dbptr, 0, sizeof(struct xgene_cle_dbptr) * DB_MAX_PTRS);
 | |
| 	dbptr[DB_RES_ACCEPT].fpsel =  def_fpsel;
 | |
| 	dbptr[DB_RES_ACCEPT].nxtfpsel = def_nxtfpsel;
 | |
| 	dbptr[DB_RES_ACCEPT].dstqid = def_qid;
 | |
| 	dbptr[DB_RES_ACCEPT].cle_priority = 1;
 | |
| 
 | |
| 	dbptr[DB_RES_DEF].fpsel = def_fpsel;
 | |
| 	dbptr[DB_RES_DEF].nxtfpsel = def_nxtfpsel;
 | |
| 	dbptr[DB_RES_DEF].dstqid = def_qid;
 | |
| 	dbptr[DB_RES_DEF].cle_priority = 7;
 | |
| 	xgene_cle_setup_def_dbptr(pdata, enet_cle, &dbptr[DB_RES_DEF],
 | |
| 				  DB_RES_ACCEPT, 7);
 | |
| 
 | |
| 	dbptr[DB_RES_DROP].drop = 1;
 | |
| 
 | |
| 	memset(&kn, 0, sizeof(kn));
 | |
| 	kn.node_type = KN;
 | |
| 	kn.num_keys = 1;
 | |
| 	kn.key[0].priority = 0;
 | |
| 	kn.key[0].result_pointer = DB_RES_ACCEPT;
 | |
| 
 | |
| 	ptree->kn = &kn;
 | |
| 	ptree->dbptr = dbptr;
 | |
| 	ptree->num_kn = 1;
 | |
| 	ptree->num_dbptr = DB_MAX_PTRS;
 | |
| 
 | |
| 	return xgene_cle_setup_ptree(pdata, enet_cle);
 | |
| }
 | |
| 
 | |
| const struct xgene_cle_ops xgene_cle3in_ops = {
 | |
| 	.cle_init = xgene_enet_cle_init,
 | |
| };
 |