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