| // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) |
| /* Copyright (C) 2015-2018 Netronome Systems, Inc. */ |
| |
| /* |
| * nfp_target.c |
| * CPP Access Width Decoder |
| * Authors: Jakub Kicinski <jakub.kicinski@netronome.com> |
| * Jason McMullan <jason.mcmullan@netronome.com> |
| * Francois H. Theron <francois.theron@netronome.com> |
| */ |
| |
| #define pr_fmt(fmt) "NFP target: " fmt |
| |
| #include <linux/bitops.h> |
| #include <linux/kernel.h> |
| #include <linux/printk.h> |
| |
| #include "nfp_cpp.h" |
| |
| #include "nfp6000/nfp6000.h" |
| |
| #define P32 1 |
| #define P64 2 |
| |
| /* This structure ONLY includes items that can be done with a read or write of |
| * 32-bit or 64-bit words. All others are not listed. |
| */ |
| |
| #define AT(_action, _token, _pull, _push) \ |
| case NFP_CPP_ID(0, (_action), (_token)): \ |
| return PUSHPULL((_pull), (_push)) |
| |
| static int target_rw(u32 cpp_id, int pp, int start, int len) |
| { |
| switch (cpp_id & NFP_CPP_ID(0, ~0, ~0)) { |
| AT(0, 0, 0, pp); |
| AT(1, 0, pp, 0); |
| AT(NFP_CPP_ACTION_RW, 0, pp, pp); |
| default: |
| return -EINVAL; |
| } |
| } |
| |
| static int nfp6000_nbi_dma(u32 cpp_id) |
| { |
| switch (cpp_id & NFP_CPP_ID(0, ~0, ~0)) { |
| AT(0, 0, 0, P64); /* ReadNbiDma */ |
| AT(1, 0, P64, 0); /* WriteNbiDma */ |
| AT(NFP_CPP_ACTION_RW, 0, P64, P64); |
| default: |
| return -EINVAL; |
| } |
| } |
| |
| static int nfp6000_nbi_stats(u32 cpp_id) |
| { |
| switch (cpp_id & NFP_CPP_ID(0, ~0, ~0)) { |
| AT(0, 0, 0, P32); /* ReadNbiStats */ |
| AT(1, 0, P32, 0); /* WriteNbiStats */ |
| AT(NFP_CPP_ACTION_RW, 0, P32, P32); |
| default: |
| return -EINVAL; |
| } |
| } |
| |
| static int nfp6000_nbi_tm(u32 cpp_id) |
| { |
| switch (cpp_id & NFP_CPP_ID(0, ~0, ~0)) { |
| AT(0, 0, 0, P64); /* ReadNbiTM */ |
| AT(1, 0, P64, 0); /* WriteNbiTM */ |
| AT(NFP_CPP_ACTION_RW, 0, P64, P64); |
| default: |
| return -EINVAL; |
| } |
| } |
| |
| static int nfp6000_nbi_ppc(u32 cpp_id) |
| { |
| switch (cpp_id & NFP_CPP_ID(0, ~0, ~0)) { |
| AT(0, 0, 0, P64); /* ReadNbiPreclassifier */ |
| AT(1, 0, P64, 0); /* WriteNbiPreclassifier */ |
| AT(NFP_CPP_ACTION_RW, 0, P64, P64); |
| default: |
| return -EINVAL; |
| } |
| } |
| |
| static int nfp6000_nbi(u32 cpp_id, u64 address) |
| { |
| u64 rel_addr = address & 0x3fFFFF; |
| |
| if (rel_addr < (1 << 20)) |
| return nfp6000_nbi_dma(cpp_id); |
| if (rel_addr < (2 << 20)) |
| return nfp6000_nbi_stats(cpp_id); |
| if (rel_addr < (3 << 20)) |
| return nfp6000_nbi_tm(cpp_id); |
| return nfp6000_nbi_ppc(cpp_id); |
| } |
| |
| /* This structure ONLY includes items that can be done with a read or write of |
| * 32-bit or 64-bit words. All others are not listed. |
| */ |
| static int nfp6000_mu_common(u32 cpp_id) |
| { |
| switch (cpp_id & NFP_CPP_ID(0, ~0, ~0)) { |
| AT(NFP_CPP_ACTION_RW, 0, P64, P64); /* read_be/write_be */ |
| AT(NFP_CPP_ACTION_RW, 1, P64, P64); /* read_le/write_le */ |
| AT(NFP_CPP_ACTION_RW, 2, P64, P64); /* read_swap_be/write_swap_be */ |
| AT(NFP_CPP_ACTION_RW, 3, P64, P64); /* read_swap_le/write_swap_le */ |
| AT(0, 0, 0, P64); /* read_be */ |
| AT(0, 1, 0, P64); /* read_le */ |
| AT(0, 2, 0, P64); /* read_swap_be */ |
| AT(0, 3, 0, P64); /* read_swap_le */ |
| AT(1, 0, P64, 0); /* write_be */ |
| AT(1, 1, P64, 0); /* write_le */ |
| AT(1, 2, P64, 0); /* write_swap_be */ |
| AT(1, 3, P64, 0); /* write_swap_le */ |
| AT(3, 0, 0, P32); /* atomic_read */ |
| AT(3, 2, P32, 0); /* mask_compare_write */ |
| AT(4, 0, P32, 0); /* atomic_write */ |
| AT(4, 2, 0, 0); /* atomic_write_imm */ |
| AT(4, 3, 0, P32); /* swap_imm */ |
| AT(5, 0, P32, 0); /* set */ |
| AT(5, 3, 0, P32); /* test_set_imm */ |
| AT(6, 0, P32, 0); /* clr */ |
| AT(6, 3, 0, P32); /* test_clr_imm */ |
| AT(7, 0, P32, 0); /* add */ |
| AT(7, 3, 0, P32); /* test_add_imm */ |
| AT(8, 0, P32, 0); /* addsat */ |
| AT(8, 3, 0, P32); /* test_subsat_imm */ |
| AT(9, 0, P32, 0); /* sub */ |
| AT(9, 3, 0, P32); /* test_sub_imm */ |
| AT(10, 0, P32, 0); /* subsat */ |
| AT(10, 3, 0, P32); /* test_subsat_imm */ |
| AT(13, 0, 0, P32); /* microq128_get */ |
| AT(13, 1, 0, P32); /* microq128_pop */ |
| AT(13, 2, P32, 0); /* microq128_put */ |
| AT(15, 0, P32, 0); /* xor */ |
| AT(15, 3, 0, P32); /* test_xor_imm */ |
| AT(28, 0, 0, P32); /* read32_be */ |
| AT(28, 1, 0, P32); /* read32_le */ |
| AT(28, 2, 0, P32); /* read32_swap_be */ |
| AT(28, 3, 0, P32); /* read32_swap_le */ |
| AT(31, 0, P32, 0); /* write32_be */ |
| AT(31, 1, P32, 0); /* write32_le */ |
| AT(31, 2, P32, 0); /* write32_swap_be */ |
| AT(31, 3, P32, 0); /* write32_swap_le */ |
| default: |
| return -EINVAL; |
| } |
| } |
| |
| static int nfp6000_mu_ctm(u32 cpp_id) |
| { |
| switch (cpp_id & NFP_CPP_ID(0, ~0, ~0)) { |
| AT(16, 1, 0, P32); /* packet_read_packet_status */ |
| AT(17, 1, 0, P32); /* packet_credit_get */ |
| AT(17, 3, 0, P64); /* packet_add_thread */ |
| AT(18, 2, 0, P64); /* packet_free_and_return_pointer */ |
| AT(18, 3, 0, P64); /* packet_return_pointer */ |
| AT(21, 0, 0, P64); /* pe_dma_to_memory_indirect */ |
| AT(21, 1, 0, P64); /* pe_dma_to_memory_indirect_swap */ |
| AT(21, 2, 0, P64); /* pe_dma_to_memory_indirect_free */ |
| AT(21, 3, 0, P64); /* pe_dma_to_memory_indirect_free_swap */ |
| default: |
| return nfp6000_mu_common(cpp_id); |
| } |
| } |
| |
| static int nfp6000_mu_emu(u32 cpp_id) |
| { |
| switch (cpp_id & NFP_CPP_ID(0, ~0, ~0)) { |
| AT(18, 0, 0, P32); /* read_queue */ |
| AT(18, 1, 0, P32); /* read_queue_ring */ |
| AT(18, 2, P32, 0); /* write_queue */ |
| AT(18, 3, P32, 0); /* write_queue_ring */ |
| AT(20, 2, P32, 0); /* journal */ |
| AT(21, 0, 0, P32); /* get */ |
| AT(21, 1, 0, P32); /* get_eop */ |
| AT(21, 2, 0, P32); /* get_freely */ |
| AT(22, 0, 0, P32); /* pop */ |
| AT(22, 1, 0, P32); /* pop_eop */ |
| AT(22, 2, 0, P32); /* pop_freely */ |
| default: |
| return nfp6000_mu_common(cpp_id); |
| } |
| } |
| |
| static int nfp6000_mu_imu(u32 cpp_id) |
| { |
| return nfp6000_mu_common(cpp_id); |
| } |
| |
| static int nfp6000_mu(u32 cpp_id, u64 address) |
| { |
| int pp; |
| |
| if (address < 0x2000000000ULL) |
| pp = nfp6000_mu_ctm(cpp_id); |
| else if (address < 0x8000000000ULL) |
| pp = nfp6000_mu_emu(cpp_id); |
| else if (address < 0x9800000000ULL) |
| pp = nfp6000_mu_ctm(cpp_id); |
| else if (address < 0x9C00000000ULL) |
| pp = nfp6000_mu_emu(cpp_id); |
| else if (address < 0xA000000000ULL) |
| pp = nfp6000_mu_imu(cpp_id); |
| else |
| pp = nfp6000_mu_ctm(cpp_id); |
| |
| return pp; |
| } |
| |
| static int nfp6000_ila(u32 cpp_id) |
| { |
| switch (cpp_id & NFP_CPP_ID(0, ~0, ~0)) { |
| AT(0, 1, 0, P32); /* read_check_error */ |
| AT(2, 0, 0, P32); /* read_int */ |
| AT(3, 0, P32, 0); /* write_int */ |
| default: |
| return target_rw(cpp_id, P32, 48, 4); |
| } |
| } |
| |
| static int nfp6000_pci(u32 cpp_id) |
| { |
| switch (cpp_id & NFP_CPP_ID(0, ~0, ~0)) { |
| AT(2, 0, 0, P32); |
| AT(3, 0, P32, 0); |
| default: |
| return target_rw(cpp_id, P32, 4, 4); |
| } |
| } |
| |
| static int nfp6000_crypto(u32 cpp_id) |
| { |
| switch (cpp_id & NFP_CPP_ID(0, ~0, ~0)) { |
| AT(2, 0, P64, 0); |
| default: |
| return target_rw(cpp_id, P64, 12, 4); |
| } |
| } |
| |
| static int nfp6000_cap_xpb(u32 cpp_id) |
| { |
| switch (cpp_id & NFP_CPP_ID(0, ~0, ~0)) { |
| AT(0, 1, 0, P32); /* RingGet */ |
| AT(0, 2, P32, 0); /* Interthread Signal */ |
| AT(1, 1, P32, 0); /* RingPut */ |
| AT(1, 2, P32, 0); /* CTNNWr */ |
| AT(2, 0, 0, P32); /* ReflectRd, signal none */ |
| AT(2, 1, 0, P32); /* ReflectRd, signal self */ |
| AT(2, 2, 0, P32); /* ReflectRd, signal remote */ |
| AT(2, 3, 0, P32); /* ReflectRd, signal both */ |
| AT(3, 0, P32, 0); /* ReflectWr, signal none */ |
| AT(3, 1, P32, 0); /* ReflectWr, signal self */ |
| AT(3, 2, P32, 0); /* ReflectWr, signal remote */ |
| AT(3, 3, P32, 0); /* ReflectWr, signal both */ |
| AT(NFP_CPP_ACTION_RW, 1, P32, P32); |
| default: |
| return target_rw(cpp_id, P32, 1, 63); |
| } |
| } |
| |
| static int nfp6000_cls(u32 cpp_id) |
| { |
| switch (cpp_id & NFP_CPP_ID(0, ~0, ~0)) { |
| AT(0, 3, P32, 0); /* xor */ |
| AT(2, 0, P32, 0); /* set */ |
| AT(2, 1, P32, 0); /* clr */ |
| AT(4, 0, P32, 0); /* add */ |
| AT(4, 1, P32, 0); /* add64 */ |
| AT(6, 0, P32, 0); /* sub */ |
| AT(6, 1, P32, 0); /* sub64 */ |
| AT(6, 2, P32, 0); /* subsat */ |
| AT(8, 2, P32, 0); /* hash_mask */ |
| AT(8, 3, P32, 0); /* hash_clear */ |
| AT(9, 0, 0, P32); /* ring_get */ |
| AT(9, 1, 0, P32); /* ring_pop */ |
| AT(9, 2, 0, P32); /* ring_get_freely */ |
| AT(9, 3, 0, P32); /* ring_pop_freely */ |
| AT(10, 0, P32, 0); /* ring_put */ |
| AT(10, 2, P32, 0); /* ring_journal */ |
| AT(14, 0, P32, 0); /* reflect_write_sig_local */ |
| AT(15, 1, 0, P32); /* reflect_read_sig_local */ |
| AT(17, 2, P32, 0); /* statisic */ |
| AT(24, 0, 0, P32); /* ring_read */ |
| AT(24, 1, P32, 0); /* ring_write */ |
| AT(25, 0, 0, P32); /* ring_workq_add_thread */ |
| AT(25, 1, P32, 0); /* ring_workq_add_work */ |
| default: |
| return target_rw(cpp_id, P32, 0, 64); |
| } |
| } |
| |
| int nfp_target_pushpull(u32 cpp_id, u64 address) |
| { |
| switch (NFP_CPP_ID_TARGET_of(cpp_id)) { |
| case NFP_CPP_TARGET_NBI: |
| return nfp6000_nbi(cpp_id, address); |
| case NFP_CPP_TARGET_QDR: |
| return target_rw(cpp_id, P32, 24, 4); |
| case NFP_CPP_TARGET_ILA: |
| return nfp6000_ila(cpp_id); |
| case NFP_CPP_TARGET_MU: |
| return nfp6000_mu(cpp_id, address); |
| case NFP_CPP_TARGET_PCIE: |
| return nfp6000_pci(cpp_id); |
| case NFP_CPP_TARGET_ARM: |
| if (address < 0x10000) |
| return target_rw(cpp_id, P64, 1, 1); |
| else |
| return target_rw(cpp_id, P32, 1, 1); |
| case NFP_CPP_TARGET_CRYPTO: |
| return nfp6000_crypto(cpp_id); |
| case NFP_CPP_TARGET_CT_XPB: |
| return nfp6000_cap_xpb(cpp_id); |
| case NFP_CPP_TARGET_CLS: |
| return nfp6000_cls(cpp_id); |
| case 0: |
| return target_rw(cpp_id, P32, 4, 4); |
| default: |
| return -EINVAL; |
| } |
| } |
| |
| #undef AT |
| #undef P32 |
| #undef P64 |
| |
| /* 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 int nfp_decode_basic(u64 addr, int *dest_island, int cpp_tgt, |
| int mode, bool addr40, int isld1, int isld0) |
| { |
| int iid_lsb, idx_lsb; |
| |
| /* This function doesn't handle MU or CTXBP */ |
| if (cpp_tgt == NFP_CPP_TARGET_MU || cpp_tgt == NFP_CPP_TARGET_CT_XPB) |
| return -EINVAL; |
| |
| 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 = (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 & BIT_ULL(idx_lsb)) |
| *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 & BIT_ULL(idx_lsb)) |
| *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 & BIT_ULL(idx_lsb)) |
| *dest_island = isld1 | (int)((addr >> iid_lsb) & 3); |
| else |
| *dest_island = isld0 | (int)((addr >> iid_lsb) & 3); |
| |
| return 0; |
| default: |
| return -EINVAL; |
| } |
| } |
| |
| static int nfp_encode_basic_qdr(u64 addr, int dest_island, int cpp_tgt, |
| int mode, bool addr40, int isld1, int isld0) |
| { |
| int v, ret; |
| |
| /* Full Island ID and channel bits overlap? */ |
| ret = nfp_decode_basic(addr, &v, cpp_tgt, mode, addr40, isld1, isld0); |
| if (ret) |
| return ret; |
| |
| /* The current address won't go where expected? */ |
| if (dest_island != -1 && dest_island != v) |
| return -EINVAL; |
| |
| /* If dest_island was -1, we don't care where it goes. */ |
| return 0; |
| } |
| |
| /* 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. |
| */ |
| static int nfp_encode_basic_search(u64 *addr, int dest_island, int *isld, |
| int iid_lsb, int idx_lsb, int v_max) |
| { |
| int i, v; |
| |
| for (i = 0; i < 2; i++) |
| for (v = 0; v < v_max; v++) { |
| if (dest_island != (isld[i] | v)) |
| continue; |
| |
| *addr &= ~GENMASK_ULL(idx_lsb, iid_lsb); |
| *addr |= ((u64)i << idx_lsb); |
| *addr |= ((u64)v << iid_lsb); |
| return 0; |
| } |
| |
| return -ENODEV; |
| } |
| |
| /* 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 int nfp_encode_basic(u64 *addr, int dest_island, int cpp_tgt, |
| int mode, bool addr40, int isld1, int isld0) |
| { |
| int iid_lsb, idx_lsb; |
| int isld[2]; |
| u64 v64; |
| |
| isld[0] = isld0; |
| isld[1] = isld1; |
| |
| /* This function doesn't handle MU or CTXBP */ |
| if (cpp_tgt == NFP_CPP_TARGET_MU || cpp_tgt == NFP_CPP_TARGET_CT_XPB) |
| return -EINVAL; |
| |
| switch (mode) { |
| case 0: |
| if (cpp_tgt == NFP_CPP_TARGET_QDR && !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. |
| */ |
| return nfp_encode_basic_qdr(*addr, cpp_tgt, dest_island, |
| mode, addr40, isld1, isld0); |
| |
| iid_lsb = addr40 ? 34 : 26; |
| /* <39:34> or <31:26> */ |
| v64 = GENMASK_ULL(iid_lsb + 5, iid_lsb); |
| *addr &= ~v64; |
| *addr |= ((u64)dest_island << iid_lsb) & v64; |
| return 0; |
| case 1: |
| if (cpp_tgt == NFP_CPP_TARGET_QDR && !addr40) |
| return nfp_encode_basic_qdr(*addr, cpp_tgt, dest_island, |
| mode, addr40, isld1, isld0); |
| |
| idx_lsb = addr40 ? 39 : 31; |
| if (dest_island == isld0) { |
| /* Only need to clear the Index bit */ |
| *addr &= ~BIT_ULL(idx_lsb); |
| return 0; |
| } |
| |
| if (dest_island == isld1) { |
| /* Only need to set the Index bit */ |
| *addr |= BIT_ULL(idx_lsb); |
| return 0; |
| } |
| |
| return -ENODEV; |
| case 2: |
| /* iid<0> = addr<30> = channel<0> |
| * channel<1> = addr<31> = Index |
| */ |
| if (cpp_tgt == NFP_CPP_TARGET_QDR && !addr40) |
| /* 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. |
| */ |
| return nfp_encode_basic_qdr(*addr, cpp_tgt, dest_island, |
| mode, addr40, isld1, isld0); |
| |
| /* 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; |
| |
| return nfp_encode_basic_search(addr, dest_island, isld, |
| iid_lsb, idx_lsb, 2); |
| case 3: |
| if (cpp_tgt == NFP_CPP_TARGET_QDR && !addr40) |
| /* iid<0> = addr<29> = data |
| * iid<1> = addr<30> = channel<0> |
| * channel<1> = addr<31> = Index |
| */ |
| return nfp_encode_basic_qdr(*addr, cpp_tgt, dest_island, |
| mode, addr40, isld1, isld0); |
| |
| isld[0] &= ~3; |
| isld[1] &= ~3; |
| |
| idx_lsb = addr40 ? 39 : 31; |
| iid_lsb = idx_lsb - 2; |
| |
| return nfp_encode_basic_search(addr, dest_island, isld, |
| iid_lsb, idx_lsb, 4); |
| default: |
| return -EINVAL; |
| } |
| } |
| |
| static int nfp_encode_mu(u64 *addr, int dest_island, int mode, |
| bool addr40, int isld1, int isld0) |
| { |
| int iid_lsb, idx_lsb, locality_lsb; |
| int isld[2]; |
| u64 v64; |
| int da; |
| |
| isld[0] = isld0; |
| isld[1] = isld1; |
| locality_lsb = nfp_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; |
| v64 = GENMASK_ULL(iid_lsb + 5, iid_lsb); |
| *addr &= ~v64; |
| *addr |= (((u64)dest_island) << iid_lsb) & v64; |
| return 0; |
| case 1: |
| if (da) { |
| iid_lsb = addr40 ? 32 : 24; |
| v64 = GENMASK_ULL(iid_lsb + 5, iid_lsb); |
| *addr &= ~v64; |
| *addr |= (((u64)dest_island) << iid_lsb) & v64; |
| return 0; |
| } |
| |
| idx_lsb = addr40 ? 37 : 29; |
| if (dest_island == isld0) { |
| *addr &= ~BIT_ULL(idx_lsb); |
| return 0; |
| } |
| |
| if (dest_island == isld1) { |
| *addr |= BIT_ULL(idx_lsb); |
| return 0; |
| } |
| |
| return -ENODEV; |
| case 2: |
| if (da) { |
| iid_lsb = addr40 ? 32 : 24; |
| v64 = GENMASK_ULL(iid_lsb + 5, iid_lsb); |
| *addr &= ~v64; |
| *addr |= (((u64)dest_island) << iid_lsb) & v64; |
| 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; |
| |
| return nfp_encode_basic_search(addr, dest_island, isld, |
| iid_lsb, idx_lsb, 2); |
| 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 effewctively ignored and just used for |
| * routing to island #0). |
| */ |
| if (dest_island > 0 && (dest_island < 24 || dest_island > 26)) { |
| *addr |= ((u64)_NIC_NFP6000_MU_LOCALITY_DIRECT) |
| << locality_lsb; |
| da = 1; |
| } |
| |
| if (da) { |
| iid_lsb = addr40 ? 32 : 24; |
| v64 = GENMASK_ULL(iid_lsb + 5, iid_lsb); |
| *addr &= ~v64; |
| *addr |= (((u64)dest_island) << iid_lsb) & v64; |
| return 0; |
| } |
| |
| isld[0] &= ~3; |
| isld[1] &= ~3; |
| |
| idx_lsb = addr40 ? 37 : 29; |
| iid_lsb = idx_lsb - 2; |
| |
| return nfp_encode_basic_search(addr, dest_island, isld, |
| iid_lsb, idx_lsb, 4); |
| default: |
| return -EINVAL; |
| } |
| } |
| |
| static int nfp_cppat_addr_encode(u64 *addr, int dest_island, int cpp_tgt, |
| int mode, bool addr40, int isld1, int isld0) |
| { |
| switch (cpp_tgt) { |
| case NFP_CPP_TARGET_NBI: |
| case NFP_CPP_TARGET_QDR: |
| case NFP_CPP_TARGET_ILA: |
| case NFP_CPP_TARGET_PCIE: |
| case NFP_CPP_TARGET_ARM: |
| case NFP_CPP_TARGET_CRYPTO: |
| case NFP_CPP_TARGET_CLS: |
| return nfp_encode_basic(addr, dest_island, cpp_tgt, mode, |
| addr40, isld1, isld0); |
| |
| case NFP_CPP_TARGET_MU: |
| return nfp_encode_mu(addr, dest_island, mode, |
| addr40, isld1, isld0); |
| |
| case NFP_CPP_TARGET_CT_XPB: |
| if (mode != 1 || addr40) |
| return -EINVAL; |
| *addr &= ~GENMASK_ULL(29, 24); |
| *addr |= ((u64)dest_island << 24) & GENMASK_ULL(29, 24); |
| return 0; |
| default: |
| return -EINVAL; |
| } |
| } |
| |
| int nfp_target_cpp(u32 cpp_island_id, u64 cpp_island_address, |
| u32 *cpp_target_id, u64 *cpp_target_address, |
| const u32 *imb_table) |
| { |
| const int island = NFP_CPP_ID_ISLAND_of(cpp_island_id); |
| const int target = NFP_CPP_ID_TARGET_of(cpp_island_id); |
| u32 imb; |
| int err; |
| |
| if (target < 0 || target >= 16) { |
| pr_err("Invalid CPP target: %d\n", target); |
| return -EINVAL; |
| } |
| |
| if (island == 0) { |
| /* Already translated */ |
| *cpp_target_id = cpp_island_id; |
| *cpp_target_address = cpp_island_address; |
| return 0; |
| } |
| |
| /* CPP + Island only allowed on systems with IMB tables */ |
| if (!imb_table) |
| return -EINVAL; |
| |
| imb = imb_table[target]; |
| |
| *cpp_target_address = cpp_island_address; |
| err = nfp_cppat_addr_encode(cpp_target_address, island, target, |
| ((imb >> 13) & 7), ((imb >> 12) & 1), |
| ((imb >> 6) & 0x3f), ((imb >> 0) & 0x3f)); |
| if (err) { |
| pr_err("Can't encode CPP address: %d\n", err); |
| return err; |
| } |
| |
| *cpp_target_id = NFP_CPP_ID(target, |
| NFP_CPP_ID_ACTION_of(cpp_island_id), |
| NFP_CPP_ID_TOKEN_of(cpp_island_id)); |
| |
| return 0; |
| } |