11590Srgrimes// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
21590Srgrimes/* Copyright (C) 2018 Netronome Systems, Inc. */
31590Srgrimes
41590Srgrimes#include <linux/bitfield.h>
51590Srgrimes#include <linux/device.h>
61590Srgrimes#include <linux/kernel.h>
71590Srgrimes#include <linux/types.h>
81590Srgrimes
91590Srgrimes#include "nfp_net_ctrl.h"
101590Srgrimes#include "nfp_net.h"
111590Srgrimes
121590Srgrimesstatic void nfp_net_tlv_caps_reset(struct nfp_net_tlv_caps *caps)
131590Srgrimes{
141590Srgrimes	memset(caps, 0, sizeof(*caps));
151590Srgrimes	caps->me_freq_mhz = 1200;
161590Srgrimes	caps->mbox_off = NFP_NET_CFG_MBOX_BASE;
171590Srgrimes	caps->mbox_len = NFP_NET_CFG_MBOX_VAL_MAX_SZ;
181590Srgrimes}
191590Srgrimes
201590Srgrimesstatic bool
211590Srgrimesnfp_net_tls_parse_crypto_ops(struct device *dev, struct nfp_net_tlv_caps *caps,
221590Srgrimes			     u8 __iomem *ctrl_mem, u8 __iomem *data,
231590Srgrimes			     unsigned int length, unsigned int offset,
241590Srgrimes			     bool rx_stream_scan)
251590Srgrimes{
261590Srgrimes	/* Ignore the legacy TLV if new one was already parsed */
271590Srgrimes	if (caps->tls_resync_ss && !rx_stream_scan)
281590Srgrimes		return true;
291590Srgrimes
301590Srgrimes	if (length < 32) {
311590Srgrimes		dev_err(dev,
321590Srgrimes			"CRYPTO OPS TLV should be at least 32B, is %dB offset:%u\n",
331590Srgrimes			length, offset);
341590Srgrimes		return false;
351590Srgrimes	}
361590Srgrimes
371590Srgrimes	caps->crypto_ops = readl(data);
381590Srgrimes	caps->crypto_enable_off = data - ctrl_mem + 16;
3928066Scharnier	caps->tls_resync_ss = rx_stream_scan;
401590Srgrimes
4128066Scharnier	return true;
4228066Scharnier}
4350477Speter
441590Srgrimesint nfp_net_tlv_caps_parse(struct device *dev, u8 __iomem *ctrl_mem,
451590Srgrimes			   struct nfp_net_tlv_caps *caps)
461590Srgrimes{
471590Srgrimes	u8 __iomem *data = ctrl_mem + NFP_NET_CFG_TLV_BASE;
4828066Scharnier	u8 __iomem *end = ctrl_mem + NFP_NET_CFG_BAR_SZ;
491590Srgrimes	u32 hdr;
501590Srgrimes
511590Srgrimes	nfp_net_tlv_caps_reset(caps);
521590Srgrimes
531590Srgrimes	hdr = readl(data);
541590Srgrimes	if (!hdr)
551590Srgrimes		return 0;
561590Srgrimes
571590Srgrimes	while (true) {
581590Srgrimes		unsigned int length, offset;
591590Srgrimes		u32 hdr = readl(data);
601590Srgrimes
611590Srgrimes		length = FIELD_GET(NFP_NET_CFG_TLV_HEADER_LENGTH, hdr);
621590Srgrimes		offset = data - ctrl_mem;
631590Srgrimes
641590Srgrimes		/* Advance past the header */
651590Srgrimes		data += 4;
661590Srgrimes
6728066Scharnier		if (length % NFP_NET_CFG_TLV_LENGTH_INC) {
681590Srgrimes			dev_err(dev, "TLV size not multiple of %u offset:%u len:%u\n",
691590Srgrimes				NFP_NET_CFG_TLV_LENGTH_INC, offset, length);
701590Srgrimes			return -EINVAL;
711590Srgrimes		}
721590Srgrimes		if (data + length > end) {
731590Srgrimes			dev_err(dev, "oversized TLV offset:%u len:%u\n",
741590Srgrimes				offset, length);
751590Srgrimes			return -EINVAL;
761590Srgrimes		}
771590Srgrimes
781590Srgrimes		switch (FIELD_GET(NFP_NET_CFG_TLV_HEADER_TYPE, hdr)) {
791590Srgrimes		case NFP_NET_CFG_TLV_TYPE_UNKNOWN:
801590Srgrimes			dev_err(dev, "NULL TLV at offset:%u\n", offset);
811590Srgrimes			return -EINVAL;
821590Srgrimes		case NFP_NET_CFG_TLV_TYPE_RESERVED:
8328066Scharnier			break;
841590Srgrimes		case NFP_NET_CFG_TLV_TYPE_END:
851590Srgrimes			if (!length)
861590Srgrimes				return 0;
871590Srgrimes
881590Srgrimes			dev_err(dev, "END TLV should be empty, has offset:%u len:%d\n",
891590Srgrimes				offset, length);
901590Srgrimes			return -EINVAL;
911590Srgrimes		case NFP_NET_CFG_TLV_TYPE_ME_FREQ:
921590Srgrimes			if (length != 4) {
931590Srgrimes				dev_err(dev,
941590Srgrimes					"ME FREQ TLV should be 4B, is %dB offset:%u\n",
951590Srgrimes					length, offset);
961590Srgrimes				return -EINVAL;
971590Srgrimes			}
981590Srgrimes
991590Srgrimes			caps->me_freq_mhz = readl(data);
1001590Srgrimes			break;
1011590Srgrimes		case NFP_NET_CFG_TLV_TYPE_MBOX:
1021590Srgrimes			if (!length) {
1031590Srgrimes				caps->mbox_off = 0;
1041590Srgrimes				caps->mbox_len = 0;
1051590Srgrimes			} else {
1061590Srgrimes				caps->mbox_off = data - ctrl_mem;
107				caps->mbox_len = length;
108			}
109			break;
110		case NFP_NET_CFG_TLV_TYPE_EXPERIMENTAL0:
111		case NFP_NET_CFG_TLV_TYPE_EXPERIMENTAL1:
112			dev_warn(dev,
113				 "experimental TLV type:%u offset:%u len:%u\n",
114				 FIELD_GET(NFP_NET_CFG_TLV_HEADER_TYPE, hdr),
115				 offset, length);
116			break;
117		case NFP_NET_CFG_TLV_TYPE_REPR_CAP:
118			if (length < 4) {
119				dev_err(dev, "REPR CAP TLV short %dB < 4B offset:%u\n",
120					length, offset);
121				return -EINVAL;
122			}
123
124			caps->repr_cap = readl(data);
125			break;
126		case NFP_NET_CFG_TLV_TYPE_MBOX_CMSG_TYPES:
127			if (length >= 4)
128				caps->mbox_cmsg_types = readl(data);
129			break;
130		case NFP_NET_CFG_TLV_TYPE_CRYPTO_OPS:
131			if (!nfp_net_tls_parse_crypto_ops(dev, caps, ctrl_mem,
132							  data, length, offset,
133							  false))
134				return -EINVAL;
135			break;
136		case NFP_NET_CFG_TLV_TYPE_VNIC_STATS:
137			if ((data - ctrl_mem) % 8) {
138				dev_warn(dev, "VNIC STATS TLV misaligned, ignoring offset:%u len:%u\n",
139					 offset, length);
140				break;
141			}
142			caps->vnic_stats_off = data - ctrl_mem;
143			caps->vnic_stats_cnt = length / 10;
144			break;
145		case NFP_NET_CFG_TLV_TYPE_CRYPTO_OPS_RX_SCAN:
146			if (!nfp_net_tls_parse_crypto_ops(dev, caps, ctrl_mem,
147							  data, length, offset,
148							  true))
149				return -EINVAL;
150			break;
151		default:
152			if (!FIELD_GET(NFP_NET_CFG_TLV_HEADER_REQUIRED, hdr))
153				break;
154
155			dev_err(dev, "unknown TLV type:%u offset:%u len:%u\n",
156				FIELD_GET(NFP_NET_CFG_TLV_HEADER_TYPE, hdr),
157				offset, length);
158			return -EINVAL;
159		}
160
161		data += length;
162		if (data + 4 > end) {
163			dev_err(dev, "reached end of BAR without END TLV\n");
164			return -EINVAL;
165		}
166	}
167
168	/* Not reached */
169	return -EINVAL;
170}
171