/* SPDX-License-Identifier: BSD-3-Clause * Copyright(c) 2018 Netronome Systems, Inc. * All rights reserved. */ #ifndef __NFP_CPPAT_H__ #define __NFP_CPPAT_H__ #include "nfp_platform.h" #include "nfp_resid.h" /* This file contains helpers for creating CPP commands * * All magic NFP-6xxx IMB 'mode' numbers here are from: * Databook (1 August 2013) * - System Overview and Connectivity * -- Internal Connectivity * --- Distributed Switch Fabric - Command Push/Pull (DSF-CPP) Bus * ---- CPP addressing * ----- Table 3.6. CPP Address Translation Mode Commands */ #define _NIC_NFP6000_MU_LOCALITY_DIRECT 2 static inline int _nfp6000_decode_basic(uint64_t addr, int *dest_island, int cpp_tgt, int mode, int addr40, int isld1, int isld0); static uint64_t _nic_mask64(int msb, int lsb, int at0) { uint64_t v; int w = msb - lsb + 1; if (w == 64) return ~(uint64_t)0; if ((lsb + w) > 64) return 0; v = (UINT64_C(1) << w) - 1; if (at0) return v; return v << lsb; } /* For VQDR, we may not modify the Channel bits, which might overlap * with the Index bit. When it does, we need to ensure that isld0 == isld1. */ static inline int _nfp6000_encode_basic(uint64_t *addr, int dest_island, int cpp_tgt, int mode, int addr40, int isld1, int isld0) { uint64_t _u64; int iid_lsb, idx_lsb; int i, v = 0; int isld[2]; isld[0] = isld0; isld[1] = isld1; switch (cpp_tgt) { case NFP6000_CPPTGT_MU: /* This function doesn't handle MU */ return NFP_ERRNO(EINVAL); case NFP6000_CPPTGT_CTXPB: /* This function doesn't handle CTXPB */ return NFP_ERRNO(EINVAL); default: break; } switch (mode) { case 0: if (cpp_tgt == NFP6000_CPPTGT_VQDR && !addr40) { /* * In this specific mode we'd rather not modify the * address but we can verify if the existing contents * will point to a valid island. */ i = _nfp6000_decode_basic(*addr, &v, cpp_tgt, mode, addr40, isld1, isld0); if (i != 0) /* Full Island ID and channel bits overlap */ return i; /* * If dest_island is invalid, the current address won't * go where expected. */ if (dest_island != -1 && dest_island != v) return NFP_ERRNO(EINVAL); /* If dest_island was -1, we don't care */ return 0; } iid_lsb = (addr40) ? 34 : 26; /* <39:34> or <31:26> */ _u64 = _nic_mask64((iid_lsb + 5), iid_lsb, 0); *addr &= ~_u64; *addr |= (((uint64_t)dest_island) << iid_lsb) & _u64; return 0; case 1: if (cpp_tgt == NFP6000_CPPTGT_VQDR && !addr40) { i = _nfp6000_decode_basic(*addr, &v, cpp_tgt, mode, addr40, isld1, isld0); if (i != 0) /* Full Island ID and channel bits overlap */ return i; /* * If dest_island is invalid, the current address won't * go where expected. */ if (dest_island != -1 && dest_island != v) return NFP_ERRNO(EINVAL); /* If dest_island was -1, we don't care */ return 0; } idx_lsb = (addr40) ? 39 : 31; if (dest_island == isld0) { /* Only need to clear the Index bit */ *addr &= ~_nic_mask64(idx_lsb, idx_lsb, 0); return 0; } if (dest_island == isld1) { /* Only need to set the Index bit */ *addr |= (UINT64_C(1) << idx_lsb); return 0; } return NFP_ERRNO(ENODEV); case 2: if (cpp_tgt == NFP6000_CPPTGT_VQDR && !addr40) { /* iid<0> = addr<30> = channel<0> */ /* channel<1> = addr<31> = Index */ /* * Special case where we allow channel bits to be set * before hand and with them select an island. * So we need to confirm that it's at least plausible. */ i = _nfp6000_decode_basic(*addr, &v, cpp_tgt, mode, addr40, isld1, isld0); if (i != 0) /* Full Island ID and channel bits overlap */ return i; /* * If dest_island is invalid, the current address won't * go where expected. */ if (dest_island != -1 && dest_island != v) return NFP_ERRNO(EINVAL); /* If dest_island was -1, we don't care */ return 0; } /* * Make sure we compare against isldN values by clearing the * LSB. This is what the silicon does. **/ isld[0] &= ~1; isld[1] &= ~1; idx_lsb = (addr40) ? 39 : 31; iid_lsb = idx_lsb - 1; /* * Try each option, take first one that fits. Not sure if we * would want to do some smarter searching and prefer 0 or non-0 * island IDs. */ for (i = 0; i < 2; i++) { for (v = 0; v < 2; v++) { if (dest_island != (isld[i] | v)) continue; *addr &= ~_nic_mask64(idx_lsb, iid_lsb, 0); *addr |= (((uint64_t)i) << idx_lsb); *addr |= (((uint64_t)v) << iid_lsb); return 0; } } return NFP_ERRNO(ENODEV); case 3: if (cpp_tgt == NFP6000_CPPTGT_VQDR && !addr40) { /* * iid<0> = addr<29> = data * iid<1> = addr<30> = channel<0> * channel<1> = addr<31> = Index */ i = _nfp6000_decode_basic(*addr, &v, cpp_tgt, mode, addr40, isld1, isld0); if (i != 0) /* Full Island ID and channel bits overlap */ return i; if (dest_island != -1 && dest_island != v) return NFP_ERRNO(EINVAL); /* If dest_island was -1, we don't care */ return 0; } isld[0] &= ~3; isld[1] &= ~3; idx_lsb = (addr40) ? 39 : 31; iid_lsb = idx_lsb - 2; for (i = 0; i < 2; i++) { for (v = 0; v < 4; v++) { if (dest_island != (isld[i] | v)) continue; *addr &= ~_nic_mask64(idx_lsb, iid_lsb, 0); *addr |= (((uint64_t)i) << idx_lsb); *addr |= (((uint64_t)v) << iid_lsb); return 0; } } return NFP_ERRNO(ENODEV); default: break; } return NFP_ERRNO(EINVAL); } static inline int _nfp6000_decode_basic(uint64_t addr, int *dest_island, int cpp_tgt, int mode, int addr40, int isld1, int isld0) { int iid_lsb, idx_lsb; switch (cpp_tgt) { case NFP6000_CPPTGT_MU: /* This function doesn't handle MU */ return NFP_ERRNO(EINVAL); case NFP6000_CPPTGT_CTXPB: /* This function doesn't handle CTXPB */ return NFP_ERRNO(EINVAL); default: break; } switch (mode) { case 0: /* * For VQDR, in this mode for 32-bit addressing it would be * islands 0, 16, 32 and 48 depending on channel and upper * address bits. Since those are not all valid islands, most * decode cases would result in bad island IDs, but we do them * anyway since this is decoding an address that is already * assumed to be used as-is to get to sram. */ iid_lsb = (addr40) ? 34 : 26; *dest_island = (int)(addr >> iid_lsb) & 0x3F; return 0; case 1: /* * For VQDR 32-bit, this would decode as: * Channel 0: island#0 * Channel 1: island#0 * Channel 2: island#1 * Channel 3: island#1 * * That would be valid as long as both islands have VQDR. * Let's allow this. */ idx_lsb = (addr40) ? 39 : 31; if (addr & _nic_mask64(idx_lsb, idx_lsb, 0)) *dest_island = isld1; else *dest_island = isld0; return 0; case 2: /* * For VQDR 32-bit: * Channel 0: (island#0 | 0) * Channel 1: (island#0 | 1) * Channel 2: (island#1 | 0) * Channel 3: (island#1 | 1) * * Make sure we compare against isldN values by clearing the * LSB. This is what the silicon does. */ isld0 &= ~1; isld1 &= ~1; idx_lsb = (addr40) ? 39 : 31; iid_lsb = idx_lsb - 1; if (addr & _nic_mask64(idx_lsb, idx_lsb, 0)) *dest_island = isld1 | (int)((addr >> iid_lsb) & 1); else *dest_island = isld0 | (int)((addr >> iid_lsb) & 1); return 0; case 3: /* * In this mode the data address starts to affect the island ID * so rather not allow it. In some really specific case one * could use this to send the upper half of the VQDR channel to * another MU, but this is getting very specific. However, as * above for mode 0, this is the decoder and the caller should * validate the resulting IID. This blindly does what the * silicon would do. */ isld0 &= ~3; isld1 &= ~3; idx_lsb = (addr40) ? 39 : 31; iid_lsb = idx_lsb - 2; if (addr & _nic_mask64(idx_lsb, idx_lsb, 0)) *dest_island = isld1 | (int)((addr >> iid_lsb) & 3); else *dest_island = isld0 | (int)((addr >> iid_lsb) & 3); return 0; default: break; } return NFP_ERRNO(EINVAL); } static inline int _nfp6000_cppat_mu_locality_lsb(int mode, int addr40) { switch (mode) { case 0: case 1: case 2: case 3: return (addr40) ? 38 : 30; default: break; } return NFP_ERRNO(EINVAL); } static inline int _nfp6000_encode_mu(uint64_t *addr, int dest_island, int mode, int addr40, int isld1, int isld0) { uint64_t _u64; int iid_lsb, idx_lsb, locality_lsb; int i, v; int isld[2]; int da; isld[0] = isld0; isld[1] = isld1; locality_lsb = _nfp6000_cppat_mu_locality_lsb(mode, addr40); if (((*addr >> locality_lsb) & 3) == _NIC_NFP6000_MU_LOCALITY_DIRECT) da = 1; else da = 0; switch (mode) { case 0: iid_lsb = (addr40) ? 32 : 24; _u64 = _nic_mask64((iid_lsb + 5), iid_lsb, 0); *addr &= ~_u64; *addr |= (((uint64_t)dest_island) << iid_lsb) & _u64; return 0; case 1: if (da) { iid_lsb = (addr40) ? 32 : 24; _u64 = _nic_mask64((iid_lsb + 5), iid_lsb, 0); *addr &= ~_u64; *addr |= (((uint64_t)dest_island) << iid_lsb) & _u64; return 0; } idx_lsb = (addr40) ? 37 : 29; if (dest_island == isld0) { *addr &= ~_nic_mask64(idx_lsb, idx_lsb, 0); return 0; } if (dest_island == isld1) { *addr |= (UINT64_C(1) << idx_lsb); return 0; } return NFP_ERRNO(ENODEV); case 2: if (da) { iid_lsb = (addr40) ? 32 : 24; _u64 = _nic_mask64((iid_lsb + 5), iid_lsb, 0); *addr &= ~_u64; *addr |= (((uint64_t)dest_island) << iid_lsb) & _u64; return 0; } /* * Make sure we compare against isldN values by clearing the * LSB. This is what the silicon does. */ isld[0] &= ~1; isld[1] &= ~1; idx_lsb = (addr40) ? 37 : 29; iid_lsb = idx_lsb - 1; /* * Try each option, take first one that fits. Not sure if we * would want to do some smarter searching and prefer 0 or * non-0 island IDs. */ for (i = 0; i < 2; i++) { for (v = 0; v < 2; v++) { if (dest_island != (isld[i] | v)) continue; *addr &= ~_nic_mask64(idx_lsb, iid_lsb, 0); *addr |= (((uint64_t)i) << idx_lsb); *addr |= (((uint64_t)v) << iid_lsb); return 0; } } return NFP_ERRNO(ENODEV); case 3: /* * Only the EMU will use 40 bit addressing. Silently set the * direct locality bit for everyone else. The SDK toolchain * uses dest_island <= 0 to test for atypical address encodings * to support access to local-island CTM with a 32-but address * (high-locality is effectively ignored and just used for * routing to island #0). */ if (dest_island > 0 && (dest_island < 24 || dest_island > 26)) { *addr |= ((uint64_t)_NIC_NFP6000_MU_LOCALITY_DIRECT) << locality_lsb; da = 1; } if (da) { iid_lsb = (addr40) ? 32 : 24; _u64 = _nic_mask64((iid_lsb + 5), iid_lsb, 0); *addr &= ~_u64; *addr |= (((uint64_t)dest_island) << iid_lsb) & _u64; return 0; } isld[0] &= ~3; isld[1] &= ~3; idx_lsb = (addr40) ? 37 : 29; iid_lsb = idx_lsb - 2; for (i = 0; i < 2; i++) { for (v = 0; v < 4; v++) { if (dest_island != (isld[i] | v)) continue; *addr &= ~_nic_mask64(idx_lsb, iid_lsb, 0); *addr |= (((uint64_t)i) << idx_lsb); *addr |= (((uint64_t)v) << iid_lsb); return 0; } } return NFP_ERRNO(ENODEV); default: break; } return NFP_ERRNO(EINVAL); } static inline int _nfp6000_decode_mu(uint64_t addr, int *dest_island, int mode, int addr40, int isld1, int isld0) { int iid_lsb, idx_lsb, locality_lsb; int da; locality_lsb = _nfp6000_cppat_mu_locality_lsb(mode, addr40); if (((addr >> locality_lsb) & 3) == _NIC_NFP6000_MU_LOCALITY_DIRECT) da = 1; else da = 0; switch (mode) { case 0: iid_lsb = (addr40) ? 32 : 24; *dest_island = (int)(addr >> iid_lsb) & 0x3F; return 0; case 1: if (da) { iid_lsb = (addr40) ? 32 : 24; *dest_island = (int)(addr >> iid_lsb) & 0x3F; return 0; } idx_lsb = (addr40) ? 37 : 29; if (addr & _nic_mask64(idx_lsb, idx_lsb, 0)) *dest_island = isld1; else *dest_island = isld0; return 0; case 2: if (da) { iid_lsb = (addr40) ? 32 : 24; *dest_island = (int)(addr >> iid_lsb) & 0x3F; return 0; } /* * Make sure we compare against isldN values by clearing the * LSB. This is what the silicon does. */ isld0 &= ~1; isld1 &= ~1; idx_lsb = (addr40) ? 37 : 29; iid_lsb = idx_lsb - 1; if (addr & _nic_mask64(idx_lsb, idx_lsb, 0)) *dest_island = isld1 | (int)((addr >> iid_lsb) & 1); else *dest_island = isld0 | (int)((addr >> iid_lsb) & 1); return 0; case 3: if (da) { iid_lsb = (addr40) ? 32 : 24; *dest_island = (int)(addr >> iid_lsb) & 0x3F; return 0; } isld0 &= ~3; isld1 &= ~3; idx_lsb = (addr40) ? 37 : 29; iid_lsb = idx_lsb - 2; if (addr & _nic_mask64(idx_lsb, idx_lsb, 0)) *dest_island = isld1 | (int)((addr >> iid_lsb) & 3); else *dest_island = isld0 | (int)((addr >> iid_lsb) & 3); return 0; default: break; } return NFP_ERRNO(EINVAL); } static inline int _nfp6000_cppat_addr_encode(uint64_t *addr, int dest_island, int cpp_tgt, int mode, int addr40, int isld1, int isld0) { switch (cpp_tgt) { case NFP6000_CPPTGT_NBI: case NFP6000_CPPTGT_VQDR: case NFP6000_CPPTGT_ILA: case NFP6000_CPPTGT_PCIE: case NFP6000_CPPTGT_ARM: case NFP6000_CPPTGT_CRYPTO: case NFP6000_CPPTGT_CLS: return _nfp6000_encode_basic(addr, dest_island, cpp_tgt, mode, addr40, isld1, isld0); case NFP6000_CPPTGT_MU: return _nfp6000_encode_mu(addr, dest_island, mode, addr40, isld1, isld0); case NFP6000_CPPTGT_CTXPB: if (mode != 1 || addr40 != 0) return NFP_ERRNO(EINVAL); *addr &= ~_nic_mask64(29, 24, 0); *addr |= (((uint64_t)dest_island) << 24) & _nic_mask64(29, 24, 0); return 0; default: break; } return NFP_ERRNO(EINVAL); } static inline int _nfp6000_cppat_addr_decode(uint64_t addr, int *dest_island, int cpp_tgt, int mode, int addr40, int isld1, int isld0) { switch (cpp_tgt) { case NFP6000_CPPTGT_NBI: case NFP6000_CPPTGT_VQDR: case NFP6000_CPPTGT_ILA: case NFP6000_CPPTGT_PCIE: case NFP6000_CPPTGT_ARM: case NFP6000_CPPTGT_CRYPTO: case NFP6000_CPPTGT_CLS: return _nfp6000_decode_basic(addr, dest_island, cpp_tgt, mode, addr40, isld1, isld0); case NFP6000_CPPTGT_MU: return _nfp6000_decode_mu(addr, dest_island, mode, addr40, isld1, isld0); case NFP6000_CPPTGT_CTXPB: if (mode != 1 || addr40 != 0) return -EINVAL; *dest_island = (int)(addr >> 24) & 0x3F; return 0; default: break; } return -EINVAL; } static inline int _nfp6000_cppat_addr_iid_clear(uint64_t *addr, int cpp_tgt, int mode, int addr40) { int iid_lsb, locality_lsb, da; switch (cpp_tgt) { case NFP6000_CPPTGT_NBI: case NFP6000_CPPTGT_VQDR: case NFP6000_CPPTGT_ILA: case NFP6000_CPPTGT_PCIE: case NFP6000_CPPTGT_ARM: case NFP6000_CPPTGT_CRYPTO: case NFP6000_CPPTGT_CLS: switch (mode) { case 0: iid_lsb = (addr40) ? 34 : 26; *addr &= ~(UINT64_C(0x3F) << iid_lsb); return 0; case 1: iid_lsb = (addr40) ? 39 : 31; *addr &= ~_nic_mask64(iid_lsb, iid_lsb, 0); return 0; case 2: iid_lsb = (addr40) ? 38 : 30; *addr &= ~_nic_mask64(iid_lsb + 1, iid_lsb, 0); return 0; case 3: iid_lsb = (addr40) ? 37 : 29; *addr &= ~_nic_mask64(iid_lsb + 2, iid_lsb, 0); return 0; default: break; } case NFP6000_CPPTGT_MU: locality_lsb = _nfp6000_cppat_mu_locality_lsb(mode, addr40); da = (((*addr >> locality_lsb) & 3) == _NIC_NFP6000_MU_LOCALITY_DIRECT); switch (mode) { case 0: iid_lsb = (addr40) ? 32 : 24; *addr &= ~(UINT64_C(0x3F) << iid_lsb); return 0; case 1: if (da) { iid_lsb = (addr40) ? 32 : 24; *addr &= ~(UINT64_C(0x3F) << iid_lsb); return 0; } iid_lsb = (addr40) ? 37 : 29; *addr &= ~_nic_mask64(iid_lsb, iid_lsb, 0); return 0; case 2: if (da) { iid_lsb = (addr40) ? 32 : 24; *addr &= ~(UINT64_C(0x3F) << iid_lsb); return 0; } iid_lsb = (addr40) ? 36 : 28; *addr &= ~_nic_mask64(iid_lsb + 1, iid_lsb, 0); return 0; case 3: if (da) { iid_lsb = (addr40) ? 32 : 24; *addr &= ~(UINT64_C(0x3F) << iid_lsb); return 0; } iid_lsb = (addr40) ? 35 : 27; *addr &= ~_nic_mask64(iid_lsb + 2, iid_lsb, 0); return 0; default: break; } case NFP6000_CPPTGT_CTXPB: if (mode != 1 || addr40 != 0) return 0; *addr &= ~(UINT64_C(0x3F) << 24); return 0; default: break; } return NFP_ERRNO(EINVAL); } #endif /* __NFP_CPPAT_H__ */