1 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
2 /* Copyright (C) 2018 Netronome Systems, Inc. */
3 
4 #include <linux/bitfield.h>
5 #include <linux/device.h>
6 #include <linux/kernel.h>
7 #include <linux/types.h>
8 
9 #include "nfp_net_ctrl.h"
10 #include "nfp_net.h"
11 
12 static void nfp_net_tlv_caps_reset(struct nfp_net_tlv_caps *caps)
13 {
14 	memset(caps, 0, sizeof(*caps));
15 	caps->me_freq_mhz = 1200;
16 	caps->mbox_off = NFP_NET_CFG_MBOX_BASE;
17 	caps->mbox_len = NFP_NET_CFG_MBOX_VAL_MAX_SZ;
18 }
19 
20 int nfp_net_tlv_caps_parse(struct device *dev, u8 __iomem *ctrl_mem,
21 			   struct nfp_net_tlv_caps *caps)
22 {
23 	u8 __iomem *data = ctrl_mem + NFP_NET_CFG_TLV_BASE;
24 	u8 __iomem *end = ctrl_mem + NFP_NET_CFG_BAR_SZ;
25 	u32 hdr;
26 
27 	nfp_net_tlv_caps_reset(caps);
28 
29 	hdr = readl(data);
30 	if (!hdr)
31 		return 0;
32 
33 	while (true) {
34 		unsigned int length, offset;
35 		u32 hdr = readl(data);
36 
37 		length = FIELD_GET(NFP_NET_CFG_TLV_HEADER_LENGTH, hdr);
38 		offset = data - ctrl_mem;
39 
40 		/* Advance past the header */
41 		data += 4;
42 
43 		if (length % NFP_NET_CFG_TLV_LENGTH_INC) {
44 			dev_err(dev, "TLV size not multiple of %u offset:%u len:%u\n",
45 				NFP_NET_CFG_TLV_LENGTH_INC, offset, length);
46 			return -EINVAL;
47 		}
48 		if (data + length > end) {
49 			dev_err(dev, "oversized TLV offset:%u len:%u\n",
50 				offset, length);
51 			return -EINVAL;
52 		}
53 
54 		switch (FIELD_GET(NFP_NET_CFG_TLV_HEADER_TYPE, hdr)) {
55 		case NFP_NET_CFG_TLV_TYPE_UNKNOWN:
56 			dev_err(dev, "NULL TLV at offset:%u\n", offset);
57 			return -EINVAL;
58 		case NFP_NET_CFG_TLV_TYPE_RESERVED:
59 			break;
60 		case NFP_NET_CFG_TLV_TYPE_END:
61 			if (!length)
62 				return 0;
63 
64 			dev_err(dev, "END TLV should be empty, has offset:%u len:%d\n",
65 				offset, length);
66 			return -EINVAL;
67 		case NFP_NET_CFG_TLV_TYPE_ME_FREQ:
68 			if (length != 4) {
69 				dev_err(dev,
70 					"ME FREQ TLV should be 4B, is %dB offset:%u\n",
71 					length, offset);
72 				return -EINVAL;
73 			}
74 
75 			caps->me_freq_mhz = readl(data);
76 			break;
77 		case NFP_NET_CFG_TLV_TYPE_MBOX:
78 			if (!length) {
79 				caps->mbox_off = 0;
80 				caps->mbox_len = 0;
81 			} else {
82 				caps->mbox_off = data - ctrl_mem;
83 				caps->mbox_len = length;
84 			}
85 			break;
86 		case NFP_NET_CFG_TLV_TYPE_EXPERIMENTAL0:
87 		case NFP_NET_CFG_TLV_TYPE_EXPERIMENTAL1:
88 			dev_warn(dev,
89 				 "experimental TLV type:%u offset:%u len:%u\n",
90 				 FIELD_GET(NFP_NET_CFG_TLV_HEADER_TYPE, hdr),
91 				 offset, length);
92 			break;
93 		case NFP_NET_CFG_TLV_TYPE_REPR_CAP:
94 			if (length < 4) {
95 				dev_err(dev, "REPR CAP TLV short %dB < 4B offset:%u\n",
96 					length, offset);
97 				return -EINVAL;
98 			}
99 
100 			caps->repr_cap = readl(data);
101 			break;
102 		case NFP_NET_CFG_TLV_TYPE_MBOX_CMSG_TYPES:
103 			if (length >= 4)
104 				caps->mbox_cmsg_types = readl(data);
105 			break;
106 		case NFP_NET_CFG_TLV_TYPE_CRYPTO_OPS:
107 			if (length < 32) {
108 				dev_err(dev,
109 					"CRYPTO OPS TLV should be at least 32B, is %dB offset:%u\n",
110 					length, offset);
111 				return -EINVAL;
112 			}
113 
114 			caps->crypto_ops = readl(data);
115 			caps->crypto_enable_off = data - ctrl_mem + 16;
116 			break;
117 		default:
118 			if (!FIELD_GET(NFP_NET_CFG_TLV_HEADER_REQUIRED, hdr))
119 				break;
120 
121 			dev_err(dev, "unknown TLV type:%u offset:%u len:%u\n",
122 				FIELD_GET(NFP_NET_CFG_TLV_HEADER_TYPE, hdr),
123 				offset, length);
124 			return -EINVAL;
125 		}
126 
127 		data += length;
128 		if (data + 4 > end) {
129 			dev_err(dev, "reached end of BAR without END TLV\n");
130 			return -EINVAL;
131 		}
132 	}
133 
134 	/* Not reached */
135 	return -EINVAL;
136 }
137