1 /* 2 * Copyright (C) 2018 Netronome Systems, Inc. 3 * 4 * This software is dual licensed under the GNU General License Version 2, 5 * June 1991 as shown in the file COPYING in the top-level directory of this 6 * source tree or the BSD 2-Clause License provided below. You have the 7 * option to license this software under the complete terms of either license. 8 * 9 * The BSD 2-Clause License: 10 * 11 * Redistribution and use in source and binary forms, with or 12 * without modification, are permitted provided that the following 13 * conditions are met: 14 * 15 * 1. Redistributions of source code must retain the above 16 * copyright notice, this list of conditions and the following 17 * disclaimer. 18 * 19 * 2. Redistributions in binary form must reproduce the above 20 * copyright notice, this list of conditions and the following 21 * disclaimer in the documentation and/or other materials 22 * provided with the distribution. 23 * 24 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 25 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 26 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 27 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 28 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 29 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 30 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 31 * SOFTWARE. 32 */ 33 34 #include <linux/bitfield.h> 35 #include <linux/device.h> 36 #include <linux/kernel.h> 37 #include <linux/types.h> 38 39 #include "nfp_net_ctrl.h" 40 #include "nfp_net.h" 41 42 static void nfp_net_tlv_caps_reset(struct nfp_net_tlv_caps *caps) 43 { 44 memset(caps, 0, sizeof(*caps)); 45 caps->me_freq_mhz = 1200; 46 caps->mbox_off = NFP_NET_CFG_MBOX_BASE; 47 caps->mbox_len = NFP_NET_CFG_MBOX_VAL_MAX_SZ; 48 } 49 50 int nfp_net_tlv_caps_parse(struct device *dev, u8 __iomem *ctrl_mem, 51 struct nfp_net_tlv_caps *caps) 52 { 53 u8 __iomem *data = ctrl_mem + NFP_NET_CFG_TLV_BASE; 54 u8 __iomem *end = ctrl_mem + NFP_NET_CFG_BAR_SZ; 55 u32 hdr; 56 57 nfp_net_tlv_caps_reset(caps); 58 59 hdr = readl(data); 60 if (!hdr) 61 return 0; 62 63 while (true) { 64 unsigned int length, offset; 65 u32 hdr = readl(data); 66 67 length = FIELD_GET(NFP_NET_CFG_TLV_HEADER_LENGTH, hdr); 68 offset = data - ctrl_mem; 69 70 /* Advance past the header */ 71 data += 4; 72 73 if (length % NFP_NET_CFG_TLV_LENGTH_INC) { 74 dev_err(dev, "TLV size not multiple of %u len:%u\n", 75 NFP_NET_CFG_TLV_LENGTH_INC, length); 76 return -EINVAL; 77 } 78 if (data + length > end) { 79 dev_err(dev, "oversized TLV offset:%u len:%u\n", 80 offset, length); 81 return -EINVAL; 82 } 83 84 switch (FIELD_GET(NFP_NET_CFG_TLV_HEADER_TYPE, hdr)) { 85 case NFP_NET_CFG_TLV_TYPE_UNKNOWN: 86 dev_err(dev, "NULL TLV at offset:%u\n", offset); 87 return -EINVAL; 88 case NFP_NET_CFG_TLV_TYPE_RESERVED: 89 break; 90 case NFP_NET_CFG_TLV_TYPE_END: 91 if (!length) 92 return 0; 93 94 dev_err(dev, "END TLV should be empty, has len:%d\n", 95 length); 96 return -EINVAL; 97 case NFP_NET_CFG_TLV_TYPE_ME_FREQ: 98 if (length != 4) { 99 dev_err(dev, 100 "ME FREQ TLV should be 4B, is %dB\n", 101 length); 102 return -EINVAL; 103 } 104 105 caps->me_freq_mhz = readl(data); 106 break; 107 case NFP_NET_CFG_TLV_TYPE_MBOX: 108 if (!length) { 109 caps->mbox_off = 0; 110 caps->mbox_len = 0; 111 } else { 112 caps->mbox_off = data - ctrl_mem; 113 caps->mbox_len = length; 114 } 115 break; 116 default: 117 if (!FIELD_GET(NFP_NET_CFG_TLV_HEADER_REQUIRED, hdr)) 118 break; 119 120 dev_err(dev, "unknown TLV type:%u offset:%u len:%u\n", 121 FIELD_GET(NFP_NET_CFG_TLV_HEADER_TYPE, hdr), 122 offset, length); 123 return -EINVAL; 124 } 125 126 data += length; 127 if (data + 4 > end) { 128 dev_err(dev, "reached end of BAR without END TLV\n"); 129 return -EINVAL; 130 } 131 } 132 133 /* Not reached */ 134 return -EINVAL; 135 } 136