linux/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_cpplib.c
<<
>>
Prefs
   1// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
   2/* Copyright (C) 2015-2018 Netronome Systems, Inc. */
   3
   4/*
   5 * nfp_cpplib.c
   6 * Library of functions to access the NFP's CPP bus
   7 * Authors: Jakub Kicinski <jakub.kicinski@netronome.com>
   8 *          Jason McMullan <jason.mcmullan@netronome.com>
   9 *          Rolf Neugebauer <rolf.neugebauer@netronome.com>
  10 */
  11
  12#include <asm/unaligned.h>
  13#include <linux/bitfield.h>
  14#include <linux/delay.h>
  15#include <linux/kernel.h>
  16#include <linux/module.h>
  17#include <linux/slab.h>
  18#include <linux/sched.h>
  19
  20#include "nfp_cpp.h"
  21#include "nfp6000/nfp6000.h"
  22#include "nfp6000/nfp_xpb.h"
  23
  24/* NFP6000 PL */
  25#define NFP_PL_DEVICE_ID                        0x00000004
  26#define   NFP_PL_DEVICE_ID_MASK                 GENMASK(7, 0)
  27#define   NFP_PL_DEVICE_PART_MASK               GENMASK(31, 16)
  28#define NFP_PL_DEVICE_MODEL_MASK                (NFP_PL_DEVICE_PART_MASK | \
  29                                                 NFP_PL_DEVICE_ID_MASK)
  30
  31/**
  32 * nfp_cpp_readl() - Read a u32 word from a CPP location
  33 * @cpp:        CPP device handle
  34 * @cpp_id:     CPP ID for operation
  35 * @address:    Address for operation
  36 * @value:      Pointer to read buffer
  37 *
  38 * Return: 0 on success, or -ERRNO
  39 */
  40int nfp_cpp_readl(struct nfp_cpp *cpp, u32 cpp_id,
  41                  unsigned long long address, u32 *value)
  42{
  43        u8 tmp[4];
  44        int n;
  45
  46        n = nfp_cpp_read(cpp, cpp_id, address, tmp, sizeof(tmp));
  47        if (n != sizeof(tmp))
  48                return n < 0 ? n : -EIO;
  49
  50        *value = get_unaligned_le32(tmp);
  51        return 0;
  52}
  53
  54/**
  55 * nfp_cpp_writel() - Write a u32 word to a CPP location
  56 * @cpp:        CPP device handle
  57 * @cpp_id:     CPP ID for operation
  58 * @address:    Address for operation
  59 * @value:      Value to write
  60 *
  61 * Return: 0 on success, or -ERRNO
  62 */
  63int nfp_cpp_writel(struct nfp_cpp *cpp, u32 cpp_id,
  64                   unsigned long long address, u32 value)
  65{
  66        u8 tmp[4];
  67        int n;
  68
  69        put_unaligned_le32(value, tmp);
  70        n = nfp_cpp_write(cpp, cpp_id, address, tmp, sizeof(tmp));
  71
  72        return n == sizeof(tmp) ? 0 : n < 0 ? n : -EIO;
  73}
  74
  75/**
  76 * nfp_cpp_readq() - Read a u64 word from a CPP location
  77 * @cpp:        CPP device handle
  78 * @cpp_id:     CPP ID for operation
  79 * @address:    Address for operation
  80 * @value:      Pointer to read buffer
  81 *
  82 * Return: 0 on success, or -ERRNO
  83 */
  84int nfp_cpp_readq(struct nfp_cpp *cpp, u32 cpp_id,
  85                  unsigned long long address, u64 *value)
  86{
  87        u8 tmp[8];
  88        int n;
  89
  90        n = nfp_cpp_read(cpp, cpp_id, address, tmp, sizeof(tmp));
  91        if (n != sizeof(tmp))
  92                return n < 0 ? n : -EIO;
  93
  94        *value = get_unaligned_le64(tmp);
  95        return 0;
  96}
  97
  98/**
  99 * nfp_cpp_writeq() - Write a u64 word to a CPP location
 100 * @cpp:        CPP device handle
 101 * @cpp_id:     CPP ID for operation
 102 * @address:    Address for operation
 103 * @value:      Value to write
 104 *
 105 * Return: 0 on success, or -ERRNO
 106 */
 107int nfp_cpp_writeq(struct nfp_cpp *cpp, u32 cpp_id,
 108                   unsigned long long address, u64 value)
 109{
 110        u8 tmp[8];
 111        int n;
 112
 113        put_unaligned_le64(value, tmp);
 114        n = nfp_cpp_write(cpp, cpp_id, address, tmp, sizeof(tmp));
 115
 116        return n == sizeof(tmp) ? 0 : n < 0 ? n : -EIO;
 117}
 118
 119/* NOTE: This code should not use nfp_xpb_* functions,
 120 * as those are model-specific
 121 */
 122int nfp_cpp_model_autodetect(struct nfp_cpp *cpp, u32 *model)
 123{
 124        u32 reg;
 125        int err;
 126
 127        err = nfp_xpb_readl(cpp, NFP_XPB_DEVICE(1, 1, 16) + NFP_PL_DEVICE_ID,
 128                            &reg);
 129        if (err < 0)
 130                return err;
 131
 132        *model = reg & NFP_PL_DEVICE_MODEL_MASK;
 133        if (*model & NFP_PL_DEVICE_ID_MASK)
 134                *model -= 0x10;
 135
 136        return 0;
 137}
 138
 139static u8 nfp_bytemask(int width, u64 addr)
 140{
 141        if (width == 8)
 142                return 0xff;
 143        else if (width == 4)
 144                return 0x0f << (addr & 4);
 145        else if (width == 2)
 146                return 0x03 << (addr & 6);
 147        else if (width == 1)
 148                return 0x01 << (addr & 7);
 149        else
 150                return 0;
 151}
 152
 153int nfp_cpp_explicit_read(struct nfp_cpp *cpp, u32 cpp_id,
 154                          u64 addr, void *buff, size_t len, int width_read)
 155{
 156        struct nfp_cpp_explicit *expl;
 157        char *tmp = buff;
 158        int err, i, incr;
 159        u8 byte_mask;
 160
 161        if (len & (width_read - 1))
 162                return -EINVAL;
 163
 164        expl = nfp_cpp_explicit_acquire(cpp);
 165        if (!expl)
 166                return -EBUSY;
 167
 168        incr = min_t(int, 16 * width_read, 128);
 169        incr = min_t(int, incr, len);
 170
 171        /* Translate a NFP_CPP_ACTION_RW to action 0 */
 172        if (NFP_CPP_ID_ACTION_of(cpp_id) == NFP_CPP_ACTION_RW)
 173                cpp_id = NFP_CPP_ID(NFP_CPP_ID_TARGET_of(cpp_id), 0,
 174                                    NFP_CPP_ID_TOKEN_of(cpp_id));
 175
 176        byte_mask = nfp_bytemask(width_read, addr);
 177
 178        nfp_cpp_explicit_set_target(expl, cpp_id,
 179                                    incr / width_read - 1, byte_mask);
 180        nfp_cpp_explicit_set_posted(expl, 1, 0, NFP_SIGNAL_PUSH,
 181                                    0, NFP_SIGNAL_NONE);
 182
 183        for (i = 0; i < len; i += incr, addr += incr, tmp += incr) {
 184                if (i + incr > len) {
 185                        incr = len - i;
 186                        nfp_cpp_explicit_set_target(expl, cpp_id,
 187                                                    incr / width_read - 1,
 188                                                    0xff);
 189                }
 190
 191                err = nfp_cpp_explicit_do(expl, addr);
 192                if (err < 0)
 193                        goto exit_release;
 194
 195                err = nfp_cpp_explicit_get(expl, tmp, incr);
 196                if (err < 0)
 197                        goto exit_release;
 198        }
 199        err = len;
 200exit_release:
 201        nfp_cpp_explicit_release(expl);
 202
 203        return err;
 204}
 205
 206int nfp_cpp_explicit_write(struct nfp_cpp *cpp, u32 cpp_id, u64 addr,
 207                           const void *buff, size_t len, int width_write)
 208{
 209        struct nfp_cpp_explicit *expl;
 210        const char *tmp = buff;
 211        int err, i, incr;
 212        u8 byte_mask;
 213
 214        if (len & (width_write - 1))
 215                return -EINVAL;
 216
 217        expl = nfp_cpp_explicit_acquire(cpp);
 218        if (!expl)
 219                return -EBUSY;
 220
 221        incr = min_t(int, 16 * width_write, 128);
 222        incr = min_t(int, incr, len);
 223
 224        /* Translate a NFP_CPP_ACTION_RW to action 1 */
 225        if (NFP_CPP_ID_ACTION_of(cpp_id) == NFP_CPP_ACTION_RW)
 226                cpp_id = NFP_CPP_ID(NFP_CPP_ID_TARGET_of(cpp_id), 1,
 227                                    NFP_CPP_ID_TOKEN_of(cpp_id));
 228
 229        byte_mask = nfp_bytemask(width_write, addr);
 230
 231        nfp_cpp_explicit_set_target(expl, cpp_id,
 232                                    incr / width_write - 1, byte_mask);
 233        nfp_cpp_explicit_set_posted(expl, 1, 0, NFP_SIGNAL_PULL,
 234                                    0, NFP_SIGNAL_NONE);
 235
 236        for (i = 0; i < len; i += incr, addr += incr, tmp += incr) {
 237                if (i + incr > len) {
 238                        incr = len - i;
 239                        nfp_cpp_explicit_set_target(expl, cpp_id,
 240                                                    incr / width_write - 1,
 241                                                    0xff);
 242                }
 243
 244                err = nfp_cpp_explicit_put(expl, tmp, incr);
 245                if (err < 0)
 246                        goto exit_release;
 247
 248                err = nfp_cpp_explicit_do(expl, addr);
 249                if (err < 0)
 250                        goto exit_release;
 251        }
 252        err = len;
 253exit_release:
 254        nfp_cpp_explicit_release(expl);
 255
 256        return err;
 257}
 258
 259/**
 260 * nfp_cpp_map_area() - Helper function to map an area
 261 * @cpp:    NFP CPP handler
 262 * @name:   Name for the area
 263 * @cpp_id: CPP ID for operation
 264 * @addr:   CPP address
 265 * @size:   Size of the area
 266 * @area:   Area handle (output)
 267 *
 268 * Map an area of IOMEM access.  To undo the effect of this function call
 269 * @nfp_cpp_area_release_free(*area).
 270 *
 271 * Return: Pointer to memory mapped area or ERR_PTR
 272 */
 273u8 __iomem *
 274nfp_cpp_map_area(struct nfp_cpp *cpp, const char *name, u32 cpp_id, u64 addr,
 275                 unsigned long size, struct nfp_cpp_area **area)
 276{
 277        u8 __iomem *res;
 278
 279        *area = nfp_cpp_area_alloc_acquire(cpp, name, cpp_id, addr, size);
 280        if (!*area)
 281                goto err_eio;
 282
 283        res = nfp_cpp_area_iomem(*area);
 284        if (!res)
 285                goto err_release_free;
 286
 287        return res;
 288
 289err_release_free:
 290        nfp_cpp_area_release_free(*area);
 291err_eio:
 292        return (u8 __iomem *)ERR_PTR(-EIO);
 293}
 294