// 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;
|
}
|