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